internal/tracer/tracer.bpf.c

// tracer.bpf.c - uretprobes for TLS libraries that shuttle bytes via
// a ring buffer to userspace. See mercemay.top/src/httptap/
//
// SPDX-License-Identifier: MIT

#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>

char LICENSE[] SEC("license") = "Dual MIT/GPL";

#define MAX_CAPTURE 2048

enum dir {
    DIR_WRITE = 1,
    DIR_READ  = 2,
};

struct event {
    __u32 pid;
    __u32 tid;
    __u8  direction;
    __u16 len;
    __u8  _pad;
    char  data[MAX_CAPTURE];
};

struct {
    __uint(type, BPF_MAP_TYPE_RINGBUF);
    __uint(max_entries, 1 << 22);
} events SEC(".maps");

struct {
    __uint(type, BPF_MAP_TYPE_HASH);
    __uint(max_entries, 1024);
    __type(key, __u32);
    __type(value, __u8);
} allowed_pids SEC(".maps");

// Per-thread argument stash so the uretprobe can see what was passed
// to the entry of SSL_write/SSL_read.
struct args {
    const void *buf;
    __u32 count;
};

struct {
    __uint(type, BPF_MAP_TYPE_HASH);
    __uint(max_entries, 4096);
    __type(key, __u64);
    __type(value, struct args);
} inflight SEC(".maps");

static __always_inline int pid_allowed(__u32 pid)
{
    return bpf_map_lookup_elem(&allowed_pids, &pid) != NULL;
}

static __always_inline int emit(__u32 pid, __u32 tid, enum dir d,
                                const void *buf, __u32 count)
{
    struct event *e;

    if (count > MAX_CAPTURE)
        count = MAX_CAPTURE;

    e = bpf_ringbuf_reserve(&events, sizeof(*e), 0);
    if (!e)
        return 0;

    e->pid = pid;
    e->tid = tid;
    e->direction = d;
    e->len = (__u16)count;
    e->_pad = 0;
    bpf_probe_read_user(e->data, count, buf);
    bpf_ringbuf_submit(e, 0);
    return 0;
}

SEC("uretprobe/SSL_write")
int BPF_KRETPROBE(on_SSL_write_ret, int ret)
{
    __u64 tgid_pid = bpf_get_current_pid_tgid();
    __u32 pid = tgid_pid >> 32;

    if (!pid_allowed(pid))
        return 0;
    if (ret <= 0)
        return 0;

    struct args *a = bpf_map_lookup_elem(&inflight, &tgid_pid);
    if (!a)
        return 0;
    emit(pid, (__u32)tgid_pid, DIR_WRITE, a->buf, (__u32)ret);
    bpf_map_delete_elem(&inflight, &tgid_pid);
    return 0;
}

SEC("uretprobe/SSL_read")
int BPF_KRETPROBE(on_SSL_read_ret, int ret)
{
    __u64 tgid_pid = bpf_get_current_pid_tgid();
    __u32 pid = tgid_pid >> 32;

    if (!pid_allowed(pid))
        return 0;
    if (ret <= 0)
        return 0;

    struct args *a = bpf_map_lookup_elem(&inflight, &tgid_pid);
    if (!a)
        return 0;
    emit(pid, (__u32)tgid_pid, DIR_READ, a->buf, (__u32)ret);
    bpf_map_delete_elem(&inflight, &tgid_pid);
    return 0;
}