> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/microsoft/eBPF-Event-Interceptor/llms.txt
> Use this file to discover all available pages before exploring further.

# UDP Monitoring

> Monitor UDP traffic using eBPF with the UDP Event Interceptor

The UDP Event Interceptor captures UDP socket activity using eBPF kernel probes. It tracks UDP send and receive operations, recording network statistics including bytes transferred, packet counts, and connection metadata for both IPv4 and IPv6.

## Overview

The UDP tracer attaches to multiple kernel functions to track UDP activity:

* `udp_sendmsg` / `udpv6_sendmsg` - Capture outbound UDP traffic
* `udp_recvmsg` / `udpv6_recvmsg` - Capture inbound UDP traffic
* `ip4_datagram_connect` / `ip6_datagram_connect` - Track socket connections
* `udp_destruct_sock` - Capture socket destruction events

## Event Structure

Each UDP event contains the following fields:

```c theme={null}
struct udp_event_t {
    uint16_t family;           // Address family (AF_INET or AF_INET6)
    uint32_t pid;              // Process ID
    uint32_t UserId;           // User ID
    uint64_t EventTime;        // Event timestamp (nanoseconds)
    uint16_t SPT;              // Source port
    uint16_t DPT;              // Destination port
    char task[16];             // Process/command name
    uint64_t rx_b;             // Bytes received
    uint64_t tx_b;             // Bytes transmitted
    uint32_t rxPkts;           // Packets received count
    uint32_t txPkts;           // Packets transmitted count
    char SADDR[64];            // Source IP address (string)
    char DADDR[64];            // Destination IP address (string)
};
```

## Initializing the UDP Tracer

<Steps>
  <Step title="Load the shared library">
    Load the UDP event interceptor library:

    ```c theme={null}
    #include <dlfcn.h>

    #define SOFILE "/opt/RealTimeKql/lib/libudpEvent.so"

    void *handle = dlopen(SOFILE, RTLD_LAZY);
    if (!handle) {
        fprintf(stderr, "Failed to load library: %s\n", dlerror());
        exit(EXIT_FAILURE);
    }
    ```
  </Step>

  <Step title="Resolve the AddProbe function">
    Get the `AddProbe` function:

    ```c theme={null}
    dlerror(); // Clear errors
    void (*AddProbe)() = dlsym(handle, "AddProbe");

    char *err = dlerror();
    if (err) {
        fprintf(stderr, "Failed to resolve AddProbe: %s\n", err);
        exit(EXIT_FAILURE);
    }
    ```

    <Note>
      Unlike TCP monitoring, UDP's `AddProbe()` takes no arguments - the BPF program is embedded in the library.
    </Note>
  </Step>

  <Step title="Resolve the DequeuePerfEvent function">
    Get the event dequeue function:

    ```c theme={null}
    dlerror();
    struct udp_event_t (*DequeuePerfEvent)() = dlsym(handle, "DequeuePerfEvent");

    err = dlerror();
    if (err) {
        fprintf(stderr, "Failed to resolve DequeuePerfEvent: %s\n", err);
        exit(EXIT_FAILURE);
    }
    ```
  </Step>

  <Step title="Resolve additional functions">
    Get status checking and cleanup functions:

    ```c theme={null}
    dlerror();
    unsigned (*getStatus)() = dlsym(handle, "getStatus");
    err = dlerror();
    if (err) {
        fprintf(stderr, "Failed to resolve getStatus: %s\n", err);
        exit(EXIT_FAILURE);
    }

    dlerror();
    void (*cleanup)() = dlsym(handle, "cleanup");
    err = dlerror();
    if (err) {
        fprintf(stderr, "Failed to resolve cleanup: %s\n", err);
        exit(EXIT_FAILURE);
    }
    ```
  </Step>

  <Step title="Attach the BPF probes">
    Call `AddProbe()` to attach all UDP monitoring probes:

    ```c theme={null}
    AddProbe();
    ```

    This internally attaches kprobes to:

    * `ip6_datagram_connect`
    * `ip4_datagram_connect`
    * `udp_recvmsg` (entry and return)
    * `udp_sendmsg`
    * `udp_destruct_sock`
    * `udpv6_recvmsg` (entry and return)
    * `udpv6_sendmsg`
  </Step>

  <Step title="Wait for initialization">
    Wait for all probes to attach:

    ```c theme={null}
    while (!getStatus()) {
        puts("Waiting for tracer initialization...");
        sleep(1);
    }
    ```
  </Step>
</Steps>

## Complete Monitoring Example

Here's a complete example based on the test implementation:

```c theme={null}
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <signal.h>
#include <unistd.h>
#include "common.h"

#define SOFILE "/opt/RealTimeKql/lib/libudpEvent.so"

void (*cleanup)();
void *handle = NULL;

void signalHandler(int signum) {
    printf("Interrupted by signal %u\n", signum);
    cleanup();
    if (handle && dlclose(handle)) {
        puts("Error closing handle, but continuing shutdown");
    }
    exit(signum);
}

void printEvent(struct udp_event_t *event) {
    if (!event) return;
    
    printf("---\n");
    printf("PID: %d\n", event->pid);
    printf("UID: %d\n", event->UserId);
    printf("Family: %d\n", event->family);
    printf("Bytes received: %lu\n", event->rx_b);
    printf("Bytes sent: %lu\n", event->tx_b);
    printf("Packets received: %u\n", event->rxPkts);
    printf("Packets sent: %u\n", event->txPkts);
    printf("Command: %s\n", event->task);
    printf("Source: %s:%d\n", event->SADDR, event->SPT);
    printf("Destination: %s:%d\n", event->DADDR, event->DPT);
    printf("Event time: %ld\n", event->EventTime);
    printf("---\n");
}

int main() {
    printf("UDP Event Monitor PID: %d\n", getpid());
    
    // Load library
    handle = dlopen(SOFILE, RTLD_LAZY);
    if (!handle) {
        fprintf(stderr, "Failed to load library\n");
        exit(EXIT_FAILURE);
    }
    
    // Resolve symbols
    void (*AddProbe)() = dlsym(handle, "AddProbe");
    struct udp_event_t (*DequeuePerfEvent)() = dlsym(handle, "DequeuePerfEvent");
    cleanup = dlsym(handle, "cleanup");
    unsigned (*getStatus)() = dlsym(handle, "getStatus");
    
    // Check for errors (simplified)
    if (!AddProbe || !DequeuePerfEvent || !cleanup || !getStatus) {
        fprintf(stderr, "Failed to resolve symbols\n");
        exit(EXIT_FAILURE);
    }
    
    // Setup signal handler
    signal(SIGINT, signalHandler);
    
    // Attach probes
    puts("Attaching UDP probes...");
    AddProbe();
    puts("Probes attached!");
    
    // Wait for initialization
    while (!getStatus()) {
        puts("Waiting on getStatus()...");
        sleep(1);
    }
    
    // Main event loop
    struct udp_event_t event;
    while (1) {
        event = DequeuePerfEvent();
        printEvent(&event);
    }
    
    return 0;
}
```

## IPv4 vs IPv6 Handling

The UDP tracer seamlessly handles both IPv4 and IPv6 traffic through separate probe points:

### IPv4 Probes

```c theme={null}
// Tracks IPv4 UDP connections
int kprobe_ip4_datagram_connect(struct pt_regs *ctx, struct sock *sk)

// Captures IPv4 UDP sends
int kprobe__udp_sendmsg(struct pt_regs *ctx, struct sock *sk, 
                        struct msghdr *msg, size_t len)

// Captures IPv4 UDP receives
int kprobe_udp_recvmsg(struct pt_regs *ctx, struct sock *sk)
int kretprobe__udp_recvmsg(struct pt_regs *ctx)
```

### IPv6 Probes

```c theme={null}
// Tracks IPv6 UDP connections
int kprobe_ip6_datagram_connect(struct pt_regs *ctx, struct sock *sk)

// Captures IPv6 UDP sends
int kprobe__udpv6_sendmsg(struct pt_regs *ctx, struct sock *sk, 
                          struct msghdr *msg, size_t len)

// Captures IPv6 UDP receives
int kprobe__udpv6_recvmsg(struct pt_regs *ctx, struct sock *sk)
int kretprobe__udpv6_recvmsg(struct pt_regs *ctx)
```

<Tip>
  The `family` field in the event structure indicates whether the traffic is IPv4 (`AF_INET` = 2) or IPv6 (`AF_INET6` = 10).
</Tip>

## Packet vs Byte Counting

The UDP tracer provides both packet counts and byte counts:

### Byte Counting

* **rx\_b**: Total bytes received across all UDP packets
* **tx\_b**: Total bytes sent across all UDP packets

Bytes are accumulated from the actual payload size reported by the kernel:

```c theme={null}
// From kretprobe__udp_recvmsg
int ret = PT_REGS_RC(ctx);  // Return value = bytes received
if (ret > 0) {
    eventPtr->rx_b += ret;
    eventPtr->rxPkts += 1;
}
```

### Packet Counting

* **rxPkts**: Number of UDP packets received
* **txPkts**: Number of UDP packets sent

Packets are counted each time send/receive operations complete:

```c theme={null}
// From kprobe__udp_sendmsg
eventPtr->tx_b += len;      // Add bytes
eventPtr->txPkts += 1;      // Increment packet count
```

<Note>
  Byte counts represent application-level payload data, not including UDP/IP headers.
</Note>

## Understanding Event Aggregation

UDP events are aggregated per socket. The tracer maintains state using a BPF hash map:

```c theme={null}
BPF_HASH(otherHash, uintptr_t, struct event_t);
```

Events are updated as traffic flows and emitted at various trigger points:

* When `udp_sendmsg` is called
* When `udp_recvmsg` completes
* When the socket is destroyed (`udp_destruct_sock`)

<Warning>
  For long-lived UDP sockets, you may receive multiple events as traffic accumulates. Each event represents a snapshot of cumulative statistics for that socket.
</Warning>

## Interpreting Event Data

### Process Information

* **pid**: Process ID that owns the socket
* **UserId**: User ID of the process (from `bpf_get_current_uid_gid()`)
* **task**: Process name (up to 16 characters)

### Connection Endpoints

* **SADDR/SPT**: Source IP address and port
* **DADDR/DPT**: Destination IP address and port
* **family**: Address family (2 = IPv4, 10 = IPv6)

<Tip>
  For unconnected UDP sockets, address information may only be available after the first send or receive operation.
</Tip>

### Timestamps

* **EventTime**: Nanosecond timestamp adjusted for system boot time

The timestamp provides absolute wall-clock time:

```c theme={null}
// Adjustment for boot time
toConsumer.EventTime = event->EventTime + notSoLongAgo;
```

## Example Output

When running the UDP tracer, you'll see output like this:

```
---
PID: 1180210
UID: 1000
Family: 10
Bytes received: 0
Bytes sent: 32
Packets received: 0
Packets sent: 1
Command: udpTraffic.sh
Source: 2001:xxx:f0:5e:aaa:a627:f45f:9c0c:42486
Destination: 2001:xxx:f0:5e:bbb:8d6f:32ef:6180:53
Event time: 1628185427077225859
---
```

This shows a DNS query (destination port 53) sent over IPv6.

## Event Queue Management

The tracer maintains an internal event queue with a maximum size:

```c theme={null}
#define MAXQSIZE 1024
```

If events are not dequeued fast enough, the tracer will shed oldest events:

```c theme={null}
if (eventDeque.size() > MAXQSIZE) {
    eventDeque.pop_front();  // Drop oldest event
    puts("Shedding UDP events..");
}
```

<Warning>
  Process events promptly in your main loop to avoid event loss during high-traffic periods.
</Warning>

## Cleanup and Shutdown

<Steps>
  <Step title="Setup signal handler">
    Register handlers for graceful shutdown:

    ```c theme={null}
    void signalHandler(int signum) {
        printf("Caught signal %d\n", signum);
        cleanup();
        if (handle && dlclose(handle)) {
            puts("Error closing handle");
        }
        exit(signum);
    }

    signal(SIGINT, signalHandler);
    ```
  </Step>

  <Step title="Call cleanup function">
    The cleanup function detaches all kprobes:

    ```c theme={null}
    cleanup();
    ```

    This detaches probes from:

    * `ip6_datagram_connect`
    * `ip4_datagram_connect`
    * `udp_recvmsg`
    * `udp_sendmsg`
    * `udp_destruct_sock`
    * `udpv6_sendmsg`
    * `udpv6_recvmsg`
    * `kretprobe__udpv6_recvmsg`
  </Step>

  <Step title="Close library handle">
    Close the dynamic library:

    ```c theme={null}
    if (dlclose(handle)) {
        fprintf(stderr, "Error closing library\n");
    }
    ```
  </Step>
</Steps>

## Best Practices

<CardGroup cols={2}>
  <Card title="High-Frequency Events" icon="bolt">
    UDP can generate events at very high rates. Ensure your processing loop is efficient.
  </Card>

  <Card title="Event Aggregation" icon="layer-group">
    Events are per-socket aggregates. Don't assume one event per packet.
  </Card>

  <Card title="Signal Handling" icon="hand">
    Always implement signal handlers to ensure proper cleanup.
  </Card>

  <Card title="Root Privileges" icon="key">
    eBPF requires root or CAP\_BPF capabilities to load probes.
  </Card>
</CardGroup>

## Troubleshooting

<AccordionGroup>
  <Accordion title="Library not found">
    Verify the library is installed:

    ```bash theme={null}
    ls -l /opt/RealTimeKql/lib/libudpEvent.so
    ```

    If not found, reinstall or update the `SOFILE` path.
  </Accordion>

  <Accordion title="Permission denied">
    Run with elevated privileges:

    ```bash theme={null}
    sudo ./udpEventTest
    ```
  </Accordion>

  <Accordion title="No events appearing">
    * Verify probes attached successfully (check console output)
    * Generate UDP traffic: `dig example.com` or `ping6 ipv6.google.com`
    * Check kernel logs: `sudo dmesg | tail`
  </Accordion>

  <Accordion title="Events being shed">
    If you see "Shedding UDP events" messages:

    * Process events faster in your main loop
    * Consider filtering events at the BPF level
    * Increase `MAXQSIZE` and recompile if needed
  </Accordion>

  <Accordion title="Missing IPv6 events">
    Ensure IPv6 is enabled:

    ```bash theme={null}
    sysctl net.ipv6.conf.all.disable_ipv6
    ```

    Should return 0 (enabled).
  </Accordion>
</AccordionGroup>

## Advanced Usage

### Filtering Specific Ports

Modify the BPF program to filter events by port:

```c theme={null}
// Add filtering in kprobe__udp_sendmsg
if (eventPtr->DPT != 53) {  // Only DNS traffic
    return 0;
}
```

### Custom Event Processing

Process events based on traffic patterns:

```c theme={null}
while (1) {
    struct udp_event_t event = DequeuePerfEvent();
    
    // Only process send-heavy sockets
    if (event.txPkts > 100) {
        analyzeTraffic(&event);
    }
    
    // Detect potential DNS tunneling
    if (event.DPT == 53 && event.tx_b > 512) {
        flagSuspiciousActivity(&event);
    }
}
```

## Next Steps

<CardGroup cols={2}>
  <Card title="TCP Monitoring" icon="network-wired" href="/guides/tcp-monitoring">
    Learn how to monitor TCP connections
  </Card>

  <Card title="Building from Source" icon="hammer" href="/guides/building-from-source">
    Customize and build the interceptor
  </Card>

  <Card title="Testing" icon="flask" href="/guides/testing">
    Run tests and verify functionality
  </Card>

  <Card title="API Reference" icon="code" href="/api/udp/overview">
    Detailed UDP API documentation
  </Card>
</CardGroup>
