// 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;
}