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

Add fallback to no kretprobes #582

Merged
merged 4 commits into from
Jan 29, 2024
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
52 changes: 52 additions & 0 deletions bpf/http_sock.c
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,58 @@ int BPF_KRETPROBE(kretprobe_tcp_recvmsg, int copied_len) {
return 0;
}

// Fall-back in case we don't see kretprobe on tcp_recvmsg in high network volume situations
SEC("socket/http_filter")
int socket__http_filter(struct __sk_buff *skb) {
protocol_info_t tcp = {};
connection_info_t conn = {};

if (!read_sk_buff(skb, &tcp, &conn)) {
return 0;
}

// ignore ACK packets
if (tcp_ack(&tcp)) {
return 0;
}

// ignore empty packets, unless it's TCP FIN or TCP RST
if (!tcp_close(&tcp) && tcp_empty(&tcp, skb)) {
return 0;
}

// sorting must happen here, before we check or set dups
sort_connection_info(&conn);

// we don't want to read the whole buffer for every packed that passes our checks, we read only a bit and check if it's trully HTTP request/response.
unsigned char buf[MIN_HTTP_SIZE] = {0};
bpf_skb_load_bytes(skb, tcp.hdr_len, (void *)buf, sizeof(buf));
// technically the read should be reversed, but eBPF verifier complains on read with variable length
u32 len = skb->len - tcp.hdr_len;
if (len > MIN_HTTP_SIZE) {
len = MIN_HTTP_SIZE;
}

u8 packet_type = 0;
if (is_http(buf, len, &packet_type)) { // we must check tcp_close second, a packet can be a close and a response
http_info_t info = {0};
info.conn_info = conn;

if (packet_type == PACKET_TYPE_REQUEST) {
u32 full_len = skb->len - tcp.hdr_len;
if (full_len > FULL_BUF_SIZE) {
full_len = FULL_BUF_SIZE;
}
read_skb_bytes(skb, tcp.hdr_len, info.buf, full_len);
bpf_dbg_printk("=== http_filter len=%d %s ===", len, buf);
//dbg_print_http_connection_info(&conn);
set_fallback_http_info(&info, &conn, skb->len - tcp.hdr_len);
}
}

return 0;
}

/*
The tracking of the clones is complicated by the fact that in container environments
the tid returned by the sys_clone call is the namespaced tid, not the host tid which
Expand Down
62 changes: 58 additions & 4 deletions bpf/http_sock.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ struct {
__uint(pinning, LIBBPF_PIN_BY_NAME);
} ongoing_http SEC(".maps");

struct {
__uint(type, BPF_MAP_TYPE_LRU_HASH);
__type(key, connection_info_t);
__type(value, http_info_t);
__uint(max_entries, MAX_CONCURRENT_SHARED_REQUESTS);
__uint(pinning, LIBBPF_PIN_BY_NAME);
} ongoing_http_fallback SEC(".maps");

// http_info_t became too big to be declared as a variable in the stack.
// We use a percpu array to keep a reusable copy of it
struct {
Expand Down Expand Up @@ -191,6 +199,13 @@ static __always_inline http_info_t *get_or_set_http_info(http_info_t *info, pid_
return bpf_map_lookup_elem(&ongoing_http, pid_conn);
}

static __always_inline void set_fallback_http_info(http_info_t *info, connection_info_t *conn, int len) {
info->start_monotime_ns = bpf_ktime_get_ns();
info->status = 0;
info->len = len;
bpf_map_update_elem(&ongoing_http_fallback, conn, info, BPF_ANY);
}

static __always_inline bool still_responding(http_info_t *info) {
return info->status != 0;
}
Expand Down Expand Up @@ -275,9 +290,16 @@ static __always_inline void handle_buf_with_connection(pid_connection_info_t *pi

http_info_t *info = get_or_set_http_info(in, pid_conn, packet_type);
if (!info) {
bpf_dbg_printk("No info, pid =%d?", pid_conn->pid);
//dbg_print_http_connection_info(&pid_conn->conn); // commented out since GitHub CI doesn't like this call
return;
bpf_dbg_printk("No info, pid =%d?, looking for fallback...", pid_conn->pid);
info = bpf_map_lookup_elem(&ongoing_http_fallback, &pid_conn->conn);
bpf_map_delete_elem(&ongoing_http_fallback, &pid_conn->conn);
if (!info) {
bpf_dbg_printk("No fallback either, giving up");
//dbg_print_http_connection_info(&pid_conn->conn); // commented out since GitHub CI doesn't like this call
return;
}
} else {
bpf_map_delete_elem(&ongoing_http_fallback, &pid_conn->conn);
}

bpf_dbg_printk("=== http_buffer_event len=%d pid=%d still_reading=%d ===", bytes_len, pid_from_pid_tgid(bpf_get_current_pid_tgid()), still_reading(info));
Expand Down Expand Up @@ -321,4 +343,36 @@ static __always_inline void handle_buf_with_connection(pid_connection_info_t *pi
}
}

#endif
#define BUF_COPY_BLOCK_SIZE 16

static __always_inline void read_skb_bytes(const void *skb, u32 offset, unsigned char *buf, const u32 len) {
u32 max = offset + len;
int b = 0;
for (; b < (FULL_BUF_SIZE/BUF_COPY_BLOCK_SIZE); b++) {
if ((offset + (BUF_COPY_BLOCK_SIZE - 1)) >= max) {
break;
}
bpf_skb_load_bytes(skb, offset, (void *)(&buf[b * BUF_COPY_BLOCK_SIZE]), BUF_COPY_BLOCK_SIZE);
offset += BUF_COPY_BLOCK_SIZE;
}

if ((b * BUF_COPY_BLOCK_SIZE) >= len) {
return;
}

// This code is messy to make sure the eBPF verifier is happy. I had to cast to signed 64bit.
s64 remainder = (s64)max - (s64)offset;

if (remainder <= 0) {
return;
}

int remaining_to_copy = (remainder < (BUF_COPY_BLOCK_SIZE - 1)) ? remainder : (BUF_COPY_BLOCK_SIZE - 1);
int space_in_buffer = (len < (b * BUF_COPY_BLOCK_SIZE)) ? 0 : len - (b * BUF_COPY_BLOCK_SIZE);

if (remaining_to_copy <= space_in_buffer) {
bpf_skb_load_bytes(skb, offset, (void *)(&buf[b * BUF_COPY_BLOCK_SIZE]), remaining_to_copy);
}
}

#endif
6 changes: 6 additions & 0 deletions pkg/internal/ebpf/httpfltr/bpf_bpfel_arm64.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file modified pkg/internal/ebpf/httpfltr/bpf_bpfel_arm64.o
Binary file not shown.
6 changes: 6 additions & 0 deletions pkg/internal/ebpf/httpfltr/bpf_bpfel_x86.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file modified pkg/internal/ebpf/httpfltr/bpf_bpfel_x86.o
Binary file not shown.
6 changes: 6 additions & 0 deletions pkg/internal/ebpf/httpfltr/bpf_debug_bpfel_arm64.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file modified pkg/internal/ebpf/httpfltr/bpf_debug_bpfel_arm64.o
Binary file not shown.
6 changes: 6 additions & 0 deletions pkg/internal/ebpf/httpfltr/bpf_debug_bpfel_x86.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file modified pkg/internal/ebpf/httpfltr/bpf_debug_bpfel_x86.o
Binary file not shown.
6 changes: 6 additions & 0 deletions pkg/internal/ebpf/httpfltr/bpf_tp_bpfel_arm64.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file modified pkg/internal/ebpf/httpfltr/bpf_tp_bpfel_arm64.o
Binary file not shown.
Loading
Loading