Skip to content

Commit

Permalink
Reuse BPF programs in multi-process mode (#471)
Browse files Browse the repository at this point in the history
  • Loading branch information
grcevski authored Dec 1, 2023
1 parent 26e3210 commit fd1de8d
Show file tree
Hide file tree
Showing 113 changed files with 3,193 additions and 929 deletions.
9 changes: 3 additions & 6 deletions bpf/go_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#define GO_COMMON_H

#include "utils.h"
#include "map_sizing.h"
#include "bpf_dbg.h"
#include "http_trace.h"
#include "ringbuf.h"
Expand All @@ -23,10 +24,6 @@

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

// TODO: make this user-configurable and modify the value from the userspace when
// loading the maps with the Cilium library
#define MAX_CONCURRENT_REQUESTS 500

// Temporary information about a function invocation. It stores the invocation time of a function
// as well as the value of registers at the invocation time. This way we can retrieve them at the
// return uprobes so we can know the values of the function arguments (which are passed as registers
Expand All @@ -44,15 +41,15 @@ struct {
__uint(type, BPF_MAP_TYPE_HASH);
__type(key, void *); // key: pointer to the goroutine
__type(value, goroutine_metadata); // value: timestamp of the goroutine creation
__uint(max_entries, MAX_CONCURRENT_REQUESTS);
__uint(max_entries, MAX_CONCURRENT_SHARED_REQUESTS);
__uint(pinning, LIBBPF_PIN_BY_NAME);
} ongoing_goroutines SEC(".maps");

struct {
__uint(type, BPF_MAP_TYPE_LRU_HASH);
__type(key, void *); // key: pointer to the goroutine
__type(value, tp_info_t); // value: traceparent info
__uint(max_entries, MAX_CONCURRENT_REQUESTS);
__uint(max_entries, MAX_CONCURRENT_SHARED_REQUESTS);
__uint(pinning, LIBBPF_PIN_BY_NAME);
} go_trace_map SEC(".maps");

Expand Down
2 changes: 0 additions & 2 deletions bpf/http_defs.h
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
#ifndef HTTP_DEFS_H
#define HTTP_DEFS_H

#define MAX_CONCURRENT_REQUESTS 10000

// Taken from linux/socket.h
#define AF_INET 2 /* Internet IP Protocol */
#define AF_INET6 10 /* IP version 6 */
Expand Down
250 changes: 0 additions & 250 deletions bpf/http_sock.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,6 @@ struct {
__type(value, recv_args_t);
} active_recv_args SEC(".maps");

// Helps us track process names of processes that exited.
// Only used with system wide instrumentation
struct {
__uint(type, BPF_MAP_TYPE_LRU_HASH);
__uint(max_entries, MAX_CONCURRENT_REQUESTS);
__type(key, u32);
__type(value, char[16]);
} dead_pids SEC(".maps");

// Used by accept to grab the sock details
SEC("kretprobe/sock_alloc")
int BPF_KRETPROBE(kretprobe_sock_alloc, struct socket *sock) {
Expand Down Expand Up @@ -221,26 +212,6 @@ int BPF_KRETPROBE(kretprobe_sys_connect, int fd)
return 0;
}

SEC("kprobe/sys_exit")
int BPF_KPROBE(kprobe_sys_exit, int status) {
u64 id = bpf_get_current_pid_tgid();

if (!valid_pid(id)) {
return 0;
}

u32 pid = pid_from_pid_tgid(id);

char comm[16];
bpf_get_current_comm(&comm, sizeof(comm));

bpf_dbg_printk("=== sys exit id=%d [%s]===", id, comm);

bpf_map_update_elem(&dead_pids, &pid, &comm, BPF_ANY); // On purpose BPF_ANY, we want to overwrite stale

return 0;
}

// Main HTTP read and write operations are handled with tcp_sendmsg and tcp_recvmsg
SEC("kprobe/tcp_sendmsg")
int BPF_KPROBE(kprobe_tcp_sendmsg, struct sock *sk, struct msghdr *msg, size_t size) {
Expand Down Expand Up @@ -335,224 +306,3 @@ int BPF_KRETPROBE(kretprobe_tcp_recvmsg, int copied_len) {

return 0;
}

// We start by looking when the SSL handshake is established. In between
// the start and the end of the SSL handshake, we'll see at least one tcp_sendmsg
// between the parties. Sandwitching this tcp_sendmsg allows us to grab the sock *
// and match it with our SSL *. The sock * will give us the connection info that is
// used by the generic HTTP filter.
SEC("uprobe/libssl.so:SSL_do_handshake")
int BPF_UPROBE(uprobe_ssl_do_handshake, void *s) {
u64 id = bpf_get_current_pid_tgid();

if (!valid_pid(id)) {
return 0;
}

bpf_dbg_printk("=== uprobe SSL_do_handshake=%d ssl=%llx===", id, s);

bpf_map_update_elem(&active_ssl_handshakes, &id, &s, BPF_ANY);

return 0;
}

SEC("uretprobe/libssl.so:SSL_do_handshake")
int BPF_URETPROBE(uretprobe_ssl_do_handshake, int ret) {
u64 id = bpf_get_current_pid_tgid();

if (!valid_pid(id)) {
return 0;
}

bpf_dbg_printk("=== uretprobe SSL_do_handshake=%d", id);

bpf_map_delete_elem(&active_ssl_handshakes, &id);

return 0;
}

// SSL read and read_ex are more less the same, but some frameworks use one or the other.
// SSL_read_ex sets an argument pointer with the number of bytes read, while SSL_read returns
// the number of bytes read.
SEC("uprobe/libssl.so:SSL_read")
int BPF_UPROBE(uprobe_ssl_read, void *ssl, const void *buf, int num) {
u64 id = bpf_get_current_pid_tgid();

if (!valid_pid(id)) {
return 0;
}

bpf_dbg_printk("=== uprobe SSL_read id=%d ssl=%llx ===", id, ssl);

ssl_args_t args = {};
args.buf = (u64)buf;
args.ssl = (u64)ssl;
args.len_ptr = 0;

bpf_map_update_elem(&active_ssl_read_args, &id, &args, BPF_ANY);
bpf_map_update_elem(&ssl_to_pid_tid, &args.ssl, &id, BPF_NOEXIST); // we must not overwrite here, remember the original thread

return 0;
}

SEC("uretprobe/libssl.so:SSL_read")
int BPF_URETPROBE(uretprobe_ssl_read, int ret) {
u64 id = bpf_get_current_pid_tgid();

if (!valid_pid(id)) {
return 0;
}

bpf_dbg_printk("=== uretprobe SSL_read id=%d ===", id);

ssl_args_t *args = bpf_map_lookup_elem(&active_ssl_read_args, &id);
bpf_map_delete_elem(&active_ssl_read_args, &id);

handle_ssl_buf(id, args, ret);
return 0;
}

SEC("uprobe/libssl.so:SSL_read_ex")
int BPF_UPROBE(uprobe_ssl_read_ex, void *ssl, const void *buf, int num, size_t *readbytes) {
u64 id = bpf_get_current_pid_tgid();

if (!valid_pid(id)) {
return 0;
}

bpf_dbg_printk("=== SSL_read_ex id=%d ===", id);

ssl_args_t args = {};
args.buf = (u64)buf;
args.ssl = (u64)ssl;
args.len_ptr = (u64)readbytes;

bpf_map_update_elem(&active_ssl_read_args, &id, &args, BPF_ANY);
bpf_map_update_elem(&ssl_to_pid_tid, &args.ssl, &id, BPF_NOEXIST); // we must not overwrite here, remember the original thread

return 0;
}

SEC("uretprobe/libssl.so:SSL_read_ex")
int BPF_URETPROBE(uretprobe_ssl_read_ex, int ret) {
u64 id = bpf_get_current_pid_tgid();

if (!valid_pid(id)) {
return 0;
}

bpf_dbg_printk("=== uretprobe SSL_read_ex id=%d ===", id);

ssl_args_t *args = bpf_map_lookup_elem(&active_ssl_read_args, &id);
bpf_map_delete_elem(&active_ssl_read_args, &id);

if (ret != 1 || !args || !args->len_ptr) {
return 0;
}

size_t read_len = 0;
bpf_probe_read(&read_len, sizeof(read_len), (void *)args->len_ptr);

handle_ssl_buf(id, args, read_len);
return 0;
}

// SSL write and write_ex are more less the same, but some frameworks use one or the other.
// SSL_write_ex sets an argument pointer with the number of bytes written, while SSL_write returns
// the number of bytes written.
SEC("uprobe/libssl.so:SSL_write")
int BPF_UPROBE(uprobe_ssl_write, void *ssl, const void *buf, int num) {
u64 id = bpf_get_current_pid_tgid();

if (!valid_pid(id)) {
return 0;
}

bpf_dbg_printk("=== uprobe SSL_write id=%d ssl=%llx ===", id, ssl);

ssl_args_t args = {};
args.buf = (u64)buf;
args.ssl = (u64)ssl;
args.len_ptr = 0;

bpf_map_update_elem(&active_ssl_write_args, &id, &args, BPF_ANY);

return 0;
}

SEC("uretprobe/libssl.so:SSL_write")
int BPF_URETPROBE(uretprobe_ssl_write, int ret) {
u64 id = bpf_get_current_pid_tgid();

if (!valid_pid(id)) {
return 0;
}

bpf_dbg_printk("=== uretprobe SSL_write id=%d ===", id);

ssl_args_t *args = bpf_map_lookup_elem(&active_ssl_write_args, &id);
bpf_map_delete_elem(&active_ssl_write_args, &id);

handle_ssl_buf(id, args, ret);
return 0;
}

SEC("uprobe/libssl.so:SSL_write_ex")
int BPF_UPROBE(uprobe_ssl_write_ex, void *ssl, const void *buf, int num, size_t *written) {
u64 id = bpf_get_current_pid_tgid();

if (!valid_pid(id)) {
return 0;
}

bpf_dbg_printk("=== SSL_write_ex id=%d ===", id);

ssl_args_t args = {};
args.buf = (u64)buf;
args.ssl = (u64)ssl;
args.len_ptr = (u64)written;

bpf_map_update_elem(&active_ssl_write_args, &id, &args, BPF_ANY);

return 0;
}

SEC("uretprobe/libssl.so:SSL_write_ex")
int BPF_URETPROBE(uretprobe_ssl_write_ex, int ret) {
u64 id = bpf_get_current_pid_tgid();

if (!valid_pid(id)) {
return 0;
}

bpf_dbg_printk("=== uretprobe SSL_write_ex id=%d ===", id);

ssl_args_t *args = bpf_map_lookup_elem(&active_ssl_write_args, &id);
bpf_map_delete_elem(&active_ssl_write_args, &id);

if (ret != 1 || !args || !args->len_ptr) {
return 0;
}

size_t wrote_len = 0;
bpf_probe_read(&wrote_len, sizeof(wrote_len), (void *)args->len_ptr);

handle_ssl_buf(id, args, wrote_len);
return 0;
}

SEC("uprobe/libssl.so:SSL_shutdown")
int BPF_UPROBE(uprobe_ssl_shutdown, void *s) {
u64 id = bpf_get_current_pid_tgid();

if (!valid_pid(id)) {
return 0;
}

bpf_dbg_printk("=== SSL_shutdown id=%d ssl=%llx ===", id, s);

bpf_map_delete_elem(&ssl_to_conn, &s);
bpf_map_delete_elem(&pid_tid_to_conn, &id);

return 0;
}
Loading

0 comments on commit fd1de8d

Please sign in to comment.