Component
OBI (eBPF instrumentation)
Problem
1. Summary
This proposal introduces network-layer sub-spans to OBI-generated traces, decomposing
request latency into discrete phases: DNS resolution, TCP connection establishment, TLS
handshake, and application processing. By surfacing these phases as child spans (or span
links) beneath existing HTTP/gRPC request spans, users gain immediate visibility into
whether observed latency originates in the network stack or in application logic — without
any application code changes.
2. Motivation
Problem Statement
When a service-to-service request is slow, operators today see a single HTTP or gRPC span
whose duration conflates network overhead with application processing time. Distinguishing
"DNS took 200 ms" from "the upstream handler was slow" requires manual packet captures or
separate monitoring tools, breaking the trace-centric workflow.
Real-World Context
This proposal is motivated by recurring issues observed in production cloud environments.
When supporting customers running microservice workloads on cloud infrastructure, a
frequently reported problem is: "requests between services are slow, but we cannot
determine whether it is the network or the application." Today, resolving such cases
requires combining distributed traces with separate network diagnostic tools (tcpdump,
dig, packet captures), which is time-consuming, requires elevated privileges, and is
often impractical in containerized/serverless environments. Native network-layer
visibility within OBI traces would dramatically reduce mean-time-to-resolution (MTTR)
for these cases.
Use Cases
| Scenario |
Value |
| DNS resolution delays (e.g., stale cache, slow resolver) |
Identify misconfigured resolvers or cache TTL issues |
| TCP connection establishment overhead (SYN retransmits, connection pool exhaustion) |
Detect capacity issues before they cascade |
| TLS handshake latency (certificate chain validation, OCSP stapling) |
Surface crypto overhead that disproportionately affects short requests |
| Cloud provider network variability |
Correlate cross-AZ latency spikes with specific network phases |
Current Gap
OBI generates flat HTTP/gRPC spans that cover the entire request lifecycle. Network-layer
timing exists in fragments (DNS events, TCP RTT metrics, SSL uprobe data) but is not
correlated to the parent request span — making root-cause analysis a manual, multi-tool
exercise.
3. Current State
OBI already captures several network-layer signals, but they are disconnected from request
traces:
| Signal |
Location |
Limitation |
| DNS detection |
bpf/generictracer/dns.h |
Produces independent events; no parent span linkage |
| TCP connect entry |
bpf/generictracer/k_tracer.c (kprobe/tcp_connect) |
Records PID→socket mapping for later correlation, but does not capture connect completion time |
| TCP RTT at close |
bpf/statsolly/k_tcp.c |
Emits k_event_stat_tcp_rtt at connection close — a metric, not per-request, not a span |
| Network flows |
bpf/netolly/flows.c (TC classifier) |
Captures packet-level flow metadata; no application-layer trace context |
| SSL read/write |
bpf/generictracer/libssl.c |
Instruments SSL_read/SSL_write for data extraction; does not time handshake |
| Trace context propagation |
HTTP header parsing + TCP Option kind=25 |
Complete — parent context is available at probe time |
| SpanLink infrastructure |
pkg/ebpf/common/ + pkg/export/otel/tracesgen/ |
Used for Go channel tracking; reusable for network event correlation |
Key insight: The eBPF probes and userspace infrastructure are largely in place. The
missing piece is timing correlation — capturing start/end timestamps for each network
phase and associating them with the owning HTTP/gRPC span.
4. Proposed Design
4.1 Target Span Hierarchy
HTTP/gRPC Request Span (existing parent)
│
├── dns.lookup (child span)
│ start: query sent end: response received
│ Attributes:
│ dns.question.name
│ dns.response_code
│ network.peer.address (resolved IP)
│ network.transport = "udp" | "tcp"
│
├── tcp.connect (child span)
│ start: connect() entry end: connection established
│ Attributes:
│ network.peer.address
│ network.peer.port
│ network.transport = "tcp"
│
├── tls.handshake (child span, when TLS is used)
│ start: SSL_do_handshake entry end: handshake complete
│ Attributes:
│ tls.protocol.version
│ tls.cipher_suite (if extractable)
│
└── [application processing — remainder of existing span body]
For connection-pooled requests (where TCP/TLS are already established), only the
application-processing portion remains — no sub-spans are generated for reused connections.
4.2 eBPF Layer Changes
TCP Connect Timing
The existing kprobe/tcp_connect in k_tracer.c already fires at connection initiation.
To capture completion:
- Option A: Add
kretprobe/inet_csk_complete_hashdance or instrument socket state
transition to TCP_ESTABLISHED via tracepoint/sock/inet_sock_set_state.
- Option B: Add
kprobe/tcp_finish_connect (called when SYN-ACK is received).
Store {pid_tgid, sock*} → start_ns in a new BPF map at kprobe/tcp_connect entry;
look it up and emit a ring buffer event with (start_ns, end_ns) at completion.
// New map: track connect start time per socket
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 4096);
__type(key, u64); // sock pointer
__type(value, u64); // start timestamp (ktime_ns)
__uint(pinning, OBI_PIN_INTERNAL);
} tcp_connect_start SEC(".maps");
DNS Correlation
The existing DNS parser in dns.h already extracts query/response data. Extend it to:
- Capture
bpf_ktime_get_ns() at DNS query send and response receive.
- Store
{pid, dns_transaction_id} → (trace_id, span_id, start_ns) so that userspace
can construct a child span with the correct parent.
This requires a small map addition (bounded by max concurrent DNS queries per host).
TLS Handshake Timing
Add uprobes for SSL_do_handshake (or SSL_connect for client-side):
SEC("uprobe/libssl.so:SSL_do_handshake")
int BPF_UPROBE(obi_uprobe_ssl_do_handshake, void *ssl) { ... }
SEC("uretprobe/libssl.so:SSL_do_handshake")
int BPF_URETPROBE(obi_uretprobe_ssl_do_handshake, int ret) { ... }
Store entry timestamp keyed by (pid_tgid, ssl*), emit timing event on return.
New Ring Buffer Event Types
enum {
k_event_tcp_connect = ..., // TCP connect start + end timestamps
k_event_tls_handshake = ..., // TLS handshake start + end timestamps
k_event_dns_correlated = ..., // DNS query/response with parent trace context
};
4.3 Userspace Changes
Event Processing (pkg/internal/ebpf/generictracer/)
- Register handlers for the new event types from the ring buffer.
- Store pending network events in a short-lived in-memory buffer keyed by
(pid, socket/connection).
Correlation Logic (pkg/ebpf/common/ or new package)
- When an HTTP/gRPC span completes, look up any pending DNS/TCP/TLS events that:
- Share the same PID
- Occurred within the span's time window
- Match the destination address/port
- Attach matched events as child spans (preferred) or SpanLinks.
Export (pkg/export/otel/tracesgen/)
- Extend
tracesgen.go to emit child spans for correlated network events.
- Reuse existing
appendSpanLinks infrastructure as a fallback relationship model.
4.4 Semantic Conventions
All attributes follow OpenTelemetry semantic conventions:
- DNS:
dns.question.name, dns.response_code — DNS Registry
- Network:
network.peer.address, network.peer.port, network.transport — Network Registry
- TLS:
tls.protocol.version, tls.cipher_suite — TLS Registry
Span names follow the {protocol}.{operation} pattern: dns.lookup, tcp.connect,
tls.handshake.
5. Implementation Phases
Phase 1: DNS Sub-Span Correlation (Low risk, high value)
Scope: Link existing DNS events to HTTP parent spans via PID + time window matching.
Changes:
- Minimal eBPF modification: add timestamp and PID context to DNS events
- Primarily Go-layer correlation logic in
pkg/ebpf/common/
- New child span emission in
tracesgen.go
Effort: ~1 week
Risk: Low — DNS detection already works; this is correlation-only.
Phase 2: TCP Connect Sub-Span (Medium risk, high value)
Scope: Capture connection establishment duration as a child span.
Changes:
- New kprobe/tracepoint for connect completion
- New BPF map (
tcp_connect_start) for socket → timestamp correlation
- New ring buffer event type
- Userspace handler and correlation logic
Effort: ~2 weeks
Risk: Medium — requires careful handling of connect timeout/failure paths and map
cleanup.
Phase 3: TLS Handshake Sub-Span (Medium risk, medium value)
Scope: Time SSL_do_handshake / SSL_connect as a child span.
Changes:
- New uprobe/uretprobe pair on
SSL_do_handshake
- Timestamp storage map keyed by
(pid_tgid, ssl*)
- Ring buffer event emission on handshake completion
Effort: ~2 weeks
Risk: Medium — library function naming varies across TLS implementations (OpenSSL,
BoringSSL, LibreSSL). May need multiple probe targets.
Phase 4: Unified Network Latency View (Higher risk)
Scope: Aggregate all sub-spans into a coherent network breakdown with configuration
controls.
Changes:
- Configuration schema for enabling/disabling individual phases
- End-to-end integration tests
- Documentation and examples
Effort: ~1 week
Risk: Higher — integration complexity across all phases; performance tuning.
6. Configuration
network_spans:
enabled: true # Master switch for all network sub-spans
dns: true # Emit dns.lookup child spans
tcp_connect: true # Emit tcp.connect child spans
tls_handshake: true # Emit tls.handshake child spans
When enabled: false, no additional eBPF probes are attached and no correlation logic
runs — zero overhead.
Individual toggles allow operators to selectively enable phases based on their
observability needs and performance budget.
7. Performance Considerations
| Factor |
Impact |
Mitigation |
| Additional kprobes/uprobes |
~50–100 ns per probe hit |
Feature is opt-in; probes only attached when enabled |
| New BPF maps |
Memory proportional to max concurrent connections |
Bounded max_entries; aggressive cleanup on timeout |
| Userspace correlation |
CPU proportional to connection rate |
Time-window-based eviction; bounded buffer size |
| Ring buffer throughput |
Additional events per connection |
Only fires on new connections, not pooled reuse |
| High-traffic scenarios |
Potential event storm during connection bursts |
Configurable rate limiting; early-exit for pooled sockets |
Connection pooling: For services using persistent connections (HTTP/2, gRPC streams,
database pools), TCP/TLS sub-spans are generated only on initial connection establishment.
Subsequent requests on the same connection skip network sub-span generation entirely.
8. Compatibility
- Minimum kernel: 5.8 (existing OBI requirement) — all proposed kprobes and
tracepoints (inet_sock_set_state, tcp_connect) are available.
- RHEL 4.18 backport:
tcp_finish_connect and inet_sock_set_state tracepoint need
verification on RHEL8/CentOS8. Fallback: use kretprobe/tcp_v4_connect which is
available on 4.18.
- No breaking changes: Existing span format is unchanged. New sub-spans are additive.
- Backward compatible: Backends that do not render child spans gracefully ignore them.
- Feature gating: Disabled by default in initial release; opt-in via configuration.
9. Alternatives Considered
A. Metrics-Only Approach
Extend existing TCP RTT metrics with per-request granularity and add DNS/TLS timing
metrics.
Rejected: Metrics lack trace correlation. Users cannot pinpoint which specific
request suffered from DNS delay without switching to a separate metrics view and
manually correlating timestamps.
B. SpanLinks Instead of Parent-Child
Use SpanLinks to loosely associate network events with HTTP spans, avoiding strict
temporal nesting requirements.
Considered as fallback: If strict parent-child proves too complex (e.g., DNS query
spans a different goroutine), SpanLinks provide a viable alternative. The existing
SpanLink infrastructure in OBI makes this low-cost to implement.
C. Userspace-Only Correlation (No New eBPF Probes)
Correlate existing independent DNS/TCP events purely by time proximity in Go code.
Rejected: In high-concurrency scenarios (many parallel connections from the same PID),
time-proximity matching produces false correlations. eBPF-level context (socket pointer,
connection tuple) is essential for accuracy.
D. Integration with netolly Pipeline
Route network sub-span data through the existing netolly TC-based flow pipeline.
Rejected for initial implementation: The netolly pipeline operates at L3/L4 without
application context (no trace IDs). Bridging the two would require significant
architectural changes. Future work could unify them.
10. Open Questions
-
Connection pool handling: Should TCP connect spans be generated only for the first
connection to a destination, or also for pool expansion events?
-
DNS caching: Should the DNS sub-span be suppressed when the resolution is served
from OS/library cache (near-zero latency)?
-
Relationship model: Is strict parent-child the right default, or should SpanLinks
be preferred to avoid issues with concurrent/out-of-order network operations?
-
Pipeline placement: Should this integrate within generictracer (sharing PID
validation and connection tracking) or be a separate eBPF program with its own
lifecycle?
-
Span naming: Should span names include the target (e.g., dns.lookup api.example.com)
or keep them generic (dns.lookup) with the target as an attribute only?
-
Minimum duration threshold: Should sub-spans below a configurable threshold
(e.g., < 1 ms) be suppressed to reduce noise?
Community feedback on these questions is welcome before implementation begins.
11. References
OBI Source References
OpenTelemetry Specifications
Related Work
Describe the solution you'd like
No response
Describe alternatives you've considered
No response
Additional context
No response
Component
OBI (eBPF instrumentation)
Problem
1. Summary
This proposal introduces network-layer sub-spans to OBI-generated traces, decomposing
request latency into discrete phases: DNS resolution, TCP connection establishment, TLS
handshake, and application processing. By surfacing these phases as child spans (or span
links) beneath existing HTTP/gRPC request spans, users gain immediate visibility into
whether observed latency originates in the network stack or in application logic — without
any application code changes.
2. Motivation
Problem Statement
When a service-to-service request is slow, operators today see a single HTTP or gRPC span
whose duration conflates network overhead with application processing time. Distinguishing
"DNS took 200 ms" from "the upstream handler was slow" requires manual packet captures or
separate monitoring tools, breaking the trace-centric workflow.
Real-World Context
This proposal is motivated by recurring issues observed in production cloud environments.
When supporting customers running microservice workloads on cloud infrastructure, a
frequently reported problem is: "requests between services are slow, but we cannot
determine whether it is the network or the application." Today, resolving such cases
requires combining distributed traces with separate network diagnostic tools (tcpdump,
dig, packet captures), which is time-consuming, requires elevated privileges, and is
often impractical in containerized/serverless environments. Native network-layer
visibility within OBI traces would dramatically reduce mean-time-to-resolution (MTTR)
for these cases.
Use Cases
Current Gap
OBI generates flat HTTP/gRPC spans that cover the entire request lifecycle. Network-layer
timing exists in fragments (DNS events, TCP RTT metrics, SSL uprobe data) but is not
correlated to the parent request span — making root-cause analysis a manual, multi-tool
exercise.
3. Current State
OBI already captures several network-layer signals, but they are disconnected from request
traces:
bpf/generictracer/dns.hbpf/generictracer/k_tracer.c(kprobe/tcp_connect)bpf/statsolly/k_tcp.ck_event_stat_tcp_rttat connection close — a metric, not per-request, not a spanbpf/netolly/flows.c(TC classifier)bpf/generictracer/libssl.cSSL_read/SSL_writefor data extraction; does not time handshakepkg/ebpf/common/+pkg/export/otel/tracesgen/Key insight: The eBPF probes and userspace infrastructure are largely in place. The
missing piece is timing correlation — capturing start/end timestamps for each network
phase and associating them with the owning HTTP/gRPC span.
4. Proposed Design
4.1 Target Span Hierarchy
For connection-pooled requests (where TCP/TLS are already established), only the
application-processing portion remains — no sub-spans are generated for reused connections.
4.2 eBPF Layer Changes
TCP Connect Timing
The existing
kprobe/tcp_connectink_tracer.calready fires at connection initiation.To capture completion:
kretprobe/inet_csk_complete_hashdanceor instrument socket statetransition to
TCP_ESTABLISHEDviatracepoint/sock/inet_sock_set_state.kprobe/tcp_finish_connect(called when SYN-ACK is received).Store
{pid_tgid, sock*} → start_nsin a new BPF map atkprobe/tcp_connectentry;look it up and emit a ring buffer event with
(start_ns, end_ns)at completion.DNS Correlation
The existing DNS parser in
dns.halready extracts query/response data. Extend it to:bpf_ktime_get_ns()at DNS query send and response receive.{pid, dns_transaction_id} → (trace_id, span_id, start_ns)so that userspacecan construct a child span with the correct parent.
This requires a small map addition (bounded by max concurrent DNS queries per host).
TLS Handshake Timing
Add uprobes for
SSL_do_handshake(orSSL_connectfor client-side):Store entry timestamp keyed by
(pid_tgid, ssl*), emit timing event on return.New Ring Buffer Event Types
4.3 Userspace Changes
Event Processing (
pkg/internal/ebpf/generictracer/)(pid, socket/connection).Correlation Logic (
pkg/ebpf/common/or new package)Export (
pkg/export/otel/tracesgen/)tracesgen.goto emit child spans for correlated network events.appendSpanLinksinfrastructure as a fallback relationship model.4.4 Semantic Conventions
All attributes follow OpenTelemetry semantic conventions:
dns.question.name,dns.response_code— DNS Registrynetwork.peer.address,network.peer.port,network.transport— Network Registrytls.protocol.version,tls.cipher_suite— TLS RegistrySpan names follow the
{protocol}.{operation}pattern:dns.lookup,tcp.connect,tls.handshake.5. Implementation Phases
Phase 1: DNS Sub-Span Correlation (Low risk, high value)
Scope: Link existing DNS events to HTTP parent spans via PID + time window matching.
Changes:
pkg/ebpf/common/tracesgen.goEffort: ~1 week
Risk: Low — DNS detection already works; this is correlation-only.
Phase 2: TCP Connect Sub-Span (Medium risk, high value)
Scope: Capture connection establishment duration as a child span.
Changes:
tcp_connect_start) for socket → timestamp correlationEffort: ~2 weeks
Risk: Medium — requires careful handling of connect timeout/failure paths and map
cleanup.
Phase 3: TLS Handshake Sub-Span (Medium risk, medium value)
Scope: Time
SSL_do_handshake/SSL_connectas a child span.Changes:
SSL_do_handshake(pid_tgid, ssl*)Effort: ~2 weeks
Risk: Medium — library function naming varies across TLS implementations (OpenSSL,
BoringSSL, LibreSSL). May need multiple probe targets.
Phase 4: Unified Network Latency View (Higher risk)
Scope: Aggregate all sub-spans into a coherent network breakdown with configuration
controls.
Changes:
Effort: ~1 week
Risk: Higher — integration complexity across all phases; performance tuning.
6. Configuration
When
enabled: false, no additional eBPF probes are attached and no correlation logicruns — zero overhead.
Individual toggles allow operators to selectively enable phases based on their
observability needs and performance budget.
7. Performance Considerations
max_entries; aggressive cleanup on timeoutConnection pooling: For services using persistent connections (HTTP/2, gRPC streams,
database pools), TCP/TLS sub-spans are generated only on initial connection establishment.
Subsequent requests on the same connection skip network sub-span generation entirely.
8. Compatibility
tracepoints (
inet_sock_set_state,tcp_connect) are available.tcp_finish_connectandinet_sock_set_statetracepoint needverification on RHEL8/CentOS8. Fallback: use
kretprobe/tcp_v4_connectwhich isavailable on 4.18.
9. Alternatives Considered
A. Metrics-Only Approach
Extend existing TCP RTT metrics with per-request granularity and add DNS/TLS timing
metrics.
Rejected: Metrics lack trace correlation. Users cannot pinpoint which specific
request suffered from DNS delay without switching to a separate metrics view and
manually correlating timestamps.
B. SpanLinks Instead of Parent-Child
Use SpanLinks to loosely associate network events with HTTP spans, avoiding strict
temporal nesting requirements.
Considered as fallback: If strict parent-child proves too complex (e.g., DNS query
spans a different goroutine), SpanLinks provide a viable alternative. The existing
SpanLink infrastructure in OBI makes this low-cost to implement.
C. Userspace-Only Correlation (No New eBPF Probes)
Correlate existing independent DNS/TCP events purely by time proximity in Go code.
Rejected: In high-concurrency scenarios (many parallel connections from the same PID),
time-proximity matching produces false correlations. eBPF-level context (socket pointer,
connection tuple) is essential for accuracy.
D. Integration with netolly Pipeline
Route network sub-span data through the existing
netollyTC-based flow pipeline.Rejected for initial implementation: The
netollypipeline operates at L3/L4 withoutapplication context (no trace IDs). Bridging the two would require significant
architectural changes. Future work could unify them.
10. Open Questions
Connection pool handling: Should TCP connect spans be generated only for the first
connection to a destination, or also for pool expansion events?
DNS caching: Should the DNS sub-span be suppressed when the resolution is served
from OS/library cache (near-zero latency)?
Relationship model: Is strict parent-child the right default, or should SpanLinks
be preferred to avoid issues with concurrent/out-of-order network operations?
Pipeline placement: Should this integrate within
generictracer(sharing PIDvalidation and connection tracking) or be a separate eBPF program with its own
lifecycle?
Span naming: Should span names include the target (e.g.,
dns.lookup api.example.com)or keep them generic (
dns.lookup) with the target as an attribute only?Minimum duration threshold: Should sub-spans below a configurable threshold
(e.g., < 1 ms) be suppressed to reduce noise?
Community feedback on these questions is welcome before implementation begins.
11. References
OBI Source References
bpf/generictracer/dns.hbpf/generictracer/k_tracer.c(line 206:kprobe/tcp_connect)bpf/statsolly/k_tcp.cbpf/generictracer/libssl.cbpf/netolly/flows.cpkg/ebpf/common/pkg/export/otel/tracesgen/tracesgen.gobpf/generictracer/maps/tcp_connection_map.hbpf/generictracer/ssl_defs.hOpenTelemetry Specifications
Related Work
Describe the solution you'd like
No response
Describe alternatives you've considered
No response
Additional context
No response