Skip to main content

Overview

Event collection is the core function of the eBPF Event Interceptor. This page details how network events flow from kernel kprobes through perf buffers to user space queues, including data structures, attribution mechanisms, and performance optimizations.
Both TCP and UDP interceptors follow similar collection patterns but differ in event structures and enrichment strategies.

Event Data Structures

TCP Event Structure

Defined in tcpEvent/common.h:19-36:
Using unsigned __int128 for addresses allows storing both IPv4 (32-bit) and IPv6 (128-bit) addresses in a single field.

TCP Consumer Event Structure

Defined in tcpEvent/common.h:39-54:
Key Differences:
  • IP addresses converted from binary to strings (e.g., “192.168.1.1”)
  • Timestamp adjusted from boot time to epoch time
  • Simplified fields relevant to consumers

UDP Event Structures

Defined in udpEvent/common.h:25-41:
Struct packing (#pragma pack(push, 1)) is critical. Without it, the C++ compiler adds padding that breaks eBPF-to-userspace data transfer.

Event Capture Flow

TCP Event Capture

UDP Event Capture

Kprobe Attachment and Event Generation

TCP Kprobe Implementation

The TCP interceptor uses a single kprobe on tcp_set_state (event.cc:187-194):
The eBPF program (loaded from external source) extracts:
  • Socket addresses and ports from struct sock*
  • PID/UID from current process context
  • Timestamp from bpf_ktime_get_ns()
  • TCP state transition information

UDP Kprobe Implementation

The UDP interceptor attaches 9 kprobes to track stateless operations:
1. Connection Tracking (2 probes):
Called when application uses connect() on UDP socket (optional for UDP).2. Send Operations (2 probes):
Capture bytes sent via sendto() or send().3. Receive Operations (4 probes):
Entry probe saves socket pointer, return probe captures bytes received.4. Cleanup (1 probe):
Submits final statistics when socket is closed.

Perf Buffer Mechanics

Buffer Declaration

TCP eBPF program:
UDP eBPF program (udpTracer.cc:84):

Opening Perf Buffer

From event.cc:196-203:
The handle_output callback is invoked for every event:

Polling Loop

From event.cc:216-219:
poll_perf_buffer() internally uses epoll() to efficiently wait for events without busy-waiting.

Event Queue Management

Queue Configuration

Both libraries use std::deque with a maximum size:

Queue Protection

Multi-threaded access requires synchronization:

Enqueue with Shedding

From event.cc:67-88:
Backpressure Handling: When events are shed, the oldest events are lost. Applications should drain the queue frequently to avoid data loss.

Dequeue with Blocking

From event.cc:113-169:
DequeuePerfEvent() blocks indefinitely until an event is available. This design simplifies consumer code but requires careful shutdown handling.
TCP events are enriched with detailed statistics via Linux netlink socket diagnostics.

Architecture

From event.cc:293-309:

Finding Socket Inodes

From event.cc:328-386:

Requesting Socket Statistics

From event.cc:414-466:
From event.cc:520-646:
Netlink events are created independently of kprobe events, providing periodic snapshots of active connections even without state changes.

Custom TCP Info Structure

From common.h:75-148:
The custom anu_tcp_info struct extends standard tcp_info to include tcpi_bytes_sent, which is essential for accurate bandwidth accounting.

Process Attribution Mechanism

Reading Process Command Line

From event.cc:661-681:
/proc/[pid]/cmdline uses null bytes as separators. The command name is the first null-terminated string.

eBPF Process Context

Inside eBPF programs, process information comes from BPF helpers:
Command names from eBPF are limited to 16 characters (TASK_COMM_LEN). The netlink enrichment path reads full command lines from /proc.

Event Deduplication and Cleanup

TCP Memory Tracking

From event.cc:29-30:
The PtrMap prevents double-free errors:

UDP Socket Tracking

From udpTracer.cc:84-86 (in eBPF program):
Purpose:
  • magic: Correlates entry and return probes for same syscall
  • otherHash: Accumulates per-socket statistics across multiple operations
Lifecycle:

Cleanup on Socket Destruction

From udpTracer.cc:182-204 (eBPF program):
When a UDP socket closes, udp_destruct_sock is called, triggering final statistics submission and map cleanup.

Performance Optimizations

Event Batching

Perf buffers naturally batch events for efficient transfer:
  • Kernel accumulates events in ring buffer
  • poll_perf_buffer() retrieves multiple events per syscall
  • Callback invoked once per event

Lock Granularity

Separate locks reduce contention between:
  • Event producers (kprobes + netlink)
  • Event consumers (DequeuePerfEvent)
  • Memory tracking (destroyEventPtr)

Zero-Copy Address Handling

From event.cc:117-121:
Netlink thread starts lazily on first DequeuePerfEvent() call, avoiding overhead if never consumed.

Timestamp Synchronization

Boot Time Calculation

From event.cc:258-283:

Timestamp Adjustment

From event.cc:128-133:
bpf_ktime_get_ns() returns monotonic time since boot. Adding the boot epoch time converts to wall-clock time for consumer applications.

Complete Event Flow Example

TCP Connection Event

UDP Send Event

Error Handling and Edge Cases

Short-Lived Processes

Problem: Process exits before /proc read Mitigation:
Consumers should check for empty task names.

IPv4 vs IPv6 Detection

Queue Overflow

Detection:
Prevention: Increase MAXQSIZE or process events faster

Next Steps

Getting Started

Build and run the eBPF Event Interceptor

TCP API Reference

Learn how to consume TCP events in your application

UDP API Reference

Learn how to consume UDP events in your application