Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reuse BPF programs in multi-process mode #471

Merged
merged 23 commits into from
Dec 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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