
Monitor DNS Traffic with eBPF
eBPF for DNS monitoring gives me precise visibility into queries and responses without touching daemon code. I attach small programs at kernel tracepoints, parse headers, and capture latency where packets flow.
I write steps you can run now: build with libbpf, attach at socket tracepoints, and export structured events to a UI like NetObserv. The approach captures both sides of a transaction and recovers response codes that many daemons omit.
This method keeps overhead low and scales from a single host to cloud clusters. You get faster triage, clearer security signals, and accurate per-response latency that you can chart and alert on.
Key Takeaways
- I use kernel attachments to watch queries, responses, and timing in real time.
- The flow records include IDs, response codes, and measured latency.
- Tools: libbpf, BCC/bpftrace, NetObserv, and a DNS-Trace proof of concept.
- No daemon recompiles: capture both sides with low overhead.
- Works on single hosts and across cloud clusters for consistent observability.
Why monitor DNS with eBPF right now
I capture packet pairs inside the kernel to give clear, verifiable operational signals.
Operational visibility: latency and error spikes map directly to user impact. I track query latency to find slow upstream resolvers and cache misses. I also surface SERVFAIL and high NXDOMAIN rates so you can fix misconfigurations before users complain.
Security pressure: attackers kept the pressure high in 2024 — over 1.5 million DNS-layer DDoS hits in Q1 and extortion up in Q2. Those numbers mean you need live signals to detect tunneling, DGA patterns, and exfiltration attempts.
- Measure operations first — latency correlates with application performance and system health.
- Watch error signals — response codes expose resolver faults you can’t see in some daemon logs.
- Spot unusual patterns — floods, odd record types, and persistent retries point to threats or bugs.
- Scale safely — continuous data lets you rate-limit clients and feed protections in cloud infrastructure.
- Skip daemon rebuilds — kernel capture avoids touching production config and speeds rollout.
NetObserv adds value by enriching flows with id, latency, and response codes. The outcome: faster RCA, cleaner dashboards, and reliable telemetry across sites.
Plan your setup: kernels, privileges, and tooling
I start with a practical checklist: confirm a recent linux kernel with BTF available and CO-RE readiness. This reduces rebuilds and smooths upgrades across environments.
Access: the agent usually needs privileged mode to enable DNSTracking in clusters. Scope privileges to specific namespaces and nodes and document capabilities in policies so security teams can approve quickly.
Pick program types based on the goal: XDP/TC for early packet handling, socket filters for per-socket capture, or tracepoints when you need kernel event context. Choose a library: libbpf for production loaders; BCC/bpftrace for quick diagnostics.
Map event export: ring buffer or perf buffer sized to peak rates to avoid drops. Budget resources—programs and maps use memory and CPU—so set size limits and rate caps.
- Platform choice: NetObserv for cluster-wide flow enrichment and UI; DNS-Trace as a libbpf PoC.
- Cloud readiness: keep the same package path for on-prem and cloud nodes; store config in version control.
- Rollback: ship a loader that detaches programs cleanly and restores state with one command.
I also recommend a short how-to for installing tooling—see install bpftool on Ubuntu—so you can validate kernel features and BTF quickly.
Implement eBPF for DNS monitoring
I pick the plane first: XDP if I need raw packet speed, TC when I want flexible classification, a socket filter for per-socket scope, or tracepoints to use kernel events. This choice shapes maps, code, and export paths.
I attach a libbpf socket program that reads struct __sk_buff, checks UDP and TCP ports, and validates L2/L3/L4 headers. I bound-check every read: verify packet length covers the DNS header before parsing. If checks fail, exit early.
Parsing: read the header, use the QR bit to detect direction, decode the Question section by reading length-prefixed labels until a zero byte, and parse type and class. Handle compression by following pointers only inside packet bounds and limit hops to avoid loops.
- Correlate by transaction ID + 5-tuple; store a timestamp on the query and compute latency on response.
- Support UDP first; add minimal TCP handling—check message length and simple reassembly windows.
- Export compact events to a ring buffer; cap size and include monotonic time for stable latency math.

Validate results and visualize network traffic
I validate captures with quick packet-level checks and a UI walkthrough so results are proof, not guesswork.
Run a baseline: use dig to issue A, AAAA, and CNAME lookups. Capture with tcpdump or Wireshark and note the transaction ids.
Then compare: confirm query and response ids match exported events and that QR, opcode, and rcode line up.
UI-driven checks with NetObserv
Use DNS Id filters to trace a single transaction. Sort by DNS Latency to find slow resolvers. Filter by Response Code to spot errors quickly.
- Table view: inspect individual fields and timestamps.
- Graphs: surface top latency and response code distributions.
- Export .pcap for packet-level proof alongside event logs.
Cluster scope: services, flows, and pods
Use Hubble or Retina to map service flows and confirm which services and pods exchange traffic. Correlate those maps to NetObserv trends.
Check drop reasons and kernel metrics when responses go missing. Store a .pcap and event log pair as proof for stakeholders and audits.
Troubleshooting and performance tuning
Cut overhead fast: apply tight in-kernel filters and avoid full payload copies unless needed. I start with simple rejects—non-UDP/TCP port 53 traffic and obvious non-query packets leave the program early.
Right-size maps next. Set conservative map sizes, watch eviction counters, and tune the ring buffer to your peak event rate. That reduces resource pressure and improves performance.
Handle edge cases
IPv6: parse headers and extension headers safely; validate offsets before reading payload. Fragmentation: skip fragments unless you implement reassembly—document that limit clearly.
TCP/TLS: parse length-prefixed TCP frames and capture only initial bytes needed to read header fields. This keeps copies small and the kernel work fast.
Detect threats and anomalies
Alert on sustained NXDOMAIN spikes, sudden high-entropy queries, or long TXT payloads used by tunneling tools. Use statistical checks in userland to flag patterns and reduce false positives.
- Filter early in kernel to cut processing time.
- Monitor map evictions and ring buffer drops.
- Keep .pcap and event logs aligned for audits.
Debug the packet path
Run pwru to find where the kernel drops packets. Layer short bpftrace scripts to time functions and spot hot paths. Compare NetObserv protocol drop reasons against kernel counters to pinpoint policy or infra blocks.
| Issue | Quick fix | Verify | Impact |
|---|---|---|---|
| High CPU from programs | Reduce sampling, limit payload copies | Measure CPU and softirq time | Lower system load |
| Ring buffer drops | Increase buffer, throttle events | Check drop counters and NetObserv rate | Fewer lost events |
| Missing responses | Trace path with pwru, check policies | Compare .pcap and exported events | Resolve blocked flows |
| False positives (DGA) | Raise alert thresholds, add entropy smoothing | Validate with sampled names and logs | Cleaner alerts |
Next steps you can take today
Begin with a short proof-of-work that produces usable event rows and packet parity. Enable NetObserv DNSTracking (set ebpf.privileged: true) and confirm the DNS Id, DNS Latency, and DNS Response Code columns appear in the UI.
Deploy DNS-Trace on one host and attach the socket loader. Watch parsed events and match them to a tcpdump capture—verify name, type, class, and rcode line up with the packet.
Use pwru and bpftrace to trace the kernel path and confirm no drops. Export a .pcap alongside event logs for quick parity checks in Wireshark.
Wire alerts on latency and error rates, tune in-kernel filters to protect system resources, and document attach steps, library versions, and rollback commands so your team can repeat the process across cluster and cloud environments.
