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

feat: grpc protocol analysis #37

Open
wants to merge 33 commits into
base: kindling-dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
cc3a15e
feat(bpf,scap,sinsp): add support for uprobe and uretprobe
yhsmer Aug 9, 2022
34ffb16
refactor(scap): change func:load_tracepoint to func:load_and_attach
yhsmer Oct 21, 2022
3cfcc68
refactor(scap_bpf, scap_func_symbol.*, NOTICES): modify func name to …
yhsmer Oct 24, 2022
4c27cd7
fix(scap_bpf): fix uprobe event array out of bound
yhsmer Oct 25, 2022
4e00d74
test(drive,libsinsp): add test for grpc
yhsmer Oct 27, 2022
e9a13bc
Merge branch 'feat/uprobe' of github.com:yhsmer/agent-libs into feat/…
yhsmer Oct 27, 2022
41c8bc9
fix(bpf,libs*): fix add thread ELF loc error, elf repate hook
yhsmer Oct 30, 2022
25faefa
fix(bpf,libs*): generate unique ftrace identifier
yhsmer Oct 30, 2022
e23212e
feat(*): achieve uprobe uretprobe
yhsmer Nov 1, 2022
0bc93d0
refactor(*): delete test demo
yhsmer Nov 1, 2022
bf9be2d
Merge branch 'kindling-dev' into feat/uprobe
yhsmer Nov 1, 2022
2f65e0a
fix(scap, sinsp): change binary file path when running in container
yhsmer Nov 30, 2022
7e1c3aa
fix(sinsp): modify the default running mode to container mode
yhsmer Nov 30, 2022
aa4acfa
remove chinese character
yhsmer Feb 17, 2023
dc4a78b
style: format code
yhsmer Feb 17, 2023
2eeb15e
merge kindling-dev
yhsmer Mar 20, 2023
4b1c461
text
yhsmer Apr 6, 2023
af31bf2
fix
yhsmer Apr 6, 2023
3d235fa
get all server and client field without insn out
yhsmer Apr 9, 2023
3c8d5eb
complete loopyWriter.writeHeader
yhsmer Apr 9, 2023
ea06176
fix
yhsmer Apr 10, 2023
46942fe
complete server operate headers
yhsmer Apr 10, 2023
53dc633
complete all
yhsmer Apr 10, 2023
fc5ef4b
complete all
yhsmer Apr 10, 2023
32044b1
feat: grpc
yhsmer Apr 10, 2023
b0f35fb
remove macro of HOST_MODE
yhsmer Apr 11, 2023
5ad1ace
fix
yhsmer Apr 13, 2023
96736b6
fix
yhsmer Apr 18, 2023
0a56aee
fix
yhsmer Apr 19, 2023
160b499
update mount path in container
yhsmer Apr 23, 2023
5ce781d
fix bug
yhsmer Apr 23, 2023
f052205
Merge branch 'kindling-dev' into feat/grpc
yhsmer Apr 25, 2023
ff81981
use env to enable uprobe and default is unenabled
yhsmer May 4, 2023
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,6 @@ driver/driver_config.h
.vscode
.cache.mk
build*
.idea/
cmake-build-debug/
kindling-falcolib-probe.tar.gz
8 changes: 6 additions & 2 deletions NOTICES
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ limitations under the License.

SUBCOMPONENTS:

- The file <common/inttypes_win.h> is licensed separately, see the header
- The file <common/inttypes_win.h> is licensed separately, see the header
of the file. Copyright (c) 2006 Alexander Chemeris.

- The files in <driver/> and its subdirectories are used to compile the
Expand All @@ -25,4 +25,8 @@ SUBCOMPONENTS:

- The files in <userspace/libsinsp/third-party/jsoncpp> and its
subdirectories are licensed separately, see the headers of each file.
Copyright (c) 2007-2010 Baptiste Lepilleur.
Copyright (c) 2007-2010 Baptiste Lepilleur.

- The two files userspace/libscap/scap_func_symbol.c and
userspace/libscap/scap_func_symbol.h are licensed with apache 2.0.
Copyright (c) 2016 GitHub, Inc.
294 changes: 293 additions & 1 deletion driver/bpf/fillers.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,38 @@ static __always_inline int bpf_##x(void *ctx) \
\
static __always_inline int __bpf_##x(struct filler_data *data) \

#define UP_FILLER_RAW(x) \
static __always_inline int __bpf_##x(struct filler_data *data); \
\
static __always_inline int bpf_##x(void *ctx) \

#define UP_FILLER(x) \
static __always_inline int __bpf_##x(struct filler_data *data); \
\
static __always_inline int bpf_##x(void *ctx) \
{ \
struct filler_data data; \
int res; \
\
res = init_filler_data(ctx, &data, false); \
if (res == PPM_SUCCESS) { \
if (!data.state->tail_ctx.len) \
write_evt_hdr(&data); \
res = __bpf_##x(&data); \
} \
\
if (res == PPM_SUCCESS) \
res = push_evt_frame(ctx, &data); \
\
if (data.state) \
data.state->tail_ctx.prev_res = res; \
\
bpf_kp_terminate_filler(&data); \
return 0; \
} \
\
static __always_inline int __bpf_##x(struct filler_data *data) \

FILLER_RAW(terminate_filler)
{
struct sysdig_bpf_per_cpu_state *state;
Expand Down Expand Up @@ -4966,7 +4998,6 @@ KP_FILLER(tcp_send_loss_probe_e)

KP_FILLER(tcp_connect_kprobe_x)
{

struct pt_regs *args = (struct pt_regs*)data->ctx;
int retval = 0;
retval= regs_return_value(args);
Expand Down Expand Up @@ -5214,4 +5245,265 @@ FILLER(cpu_analysis_e, false)
{
return 0;
}

static __always_inline int32_t get_fd_from_conn_intf_core(struct go_interface conn_intf)
{
void *fd_ptr;
bpf_probe_read(&fd_ptr, sizeof(fd_ptr), conn_intf.ptr);

int64_t sysfd;
bpf_probe_read(&sysfd, sizeof(int64_t), fd_ptr + 16);
return sysfd;
}

static __always_inline int32_t get_fd_from_http2_Framer(const void *framer_ptr)
{
struct go_interface io_writer_interface;
// At this point, we have the following struct:
// go.itab.*google.golang.org/grpc/internal/transport.bufWriter,io.Writer
bpf_probe_read(&io_writer_interface, sizeof(io_writer_interface), framer_ptr + 112);

struct go_interface conn_intf;
bpf_probe_read(&conn_intf, sizeof(conn_intf), io_writer_interface.ptr + 40);

return get_fd_from_conn_intf_core(conn_intf);
}

static __always_inline void parse_header_field(char *dst, int *size, const void *header_field_ptr)
{
struct gostring str = {};
bpf_probe_read(&str, sizeof(str), header_field_ptr);
if (str.len <= 0)
{
*size = 0;
return;
}

*size = (int)min(str.len, HEADER_FIELD_STR_SIZE);

bpf_probe_read(dst, *size, str.ptr);
}

// encode grpc-header, then send
UP_FILLER(probe_loopy_writer_write_header){
struct pt_regs* regs = (struct pt_regs*) data->ctx;
const void *sp = (const void *)_READ(regs->sp);

uint32_t stream_id = 0;
bpf_probe_read(&stream_id, sizeof(uint32_t), sp + 16);

bool end_stream = false;
bpf_probe_read(&end_stream, sizeof(bool), sp + 20);

void *fields_ptr;
bpf_probe_read(&fields_ptr, sizeof(void *), sp + 24);

int64_t fields_len;
bpf_probe_read(&fields_len, sizeof(int64_t), sp + 24 + 8);

void *loopy_writer_ptr = NULL;
bpf_probe_read(&loopy_writer_ptr, sizeof(loopy_writer_ptr), sp + 8);

void *framer_ptr;
bpf_probe_read(&framer_ptr, sizeof(framer_ptr), loopy_writer_ptr + 40);

struct go_grpc_framer_t go_grpc_framer;
bpf_probe_read(&go_grpc_framer, sizeof(go_grpc_framer), framer_ptr);

const int32_t fd = get_fd_from_http2_Framer(go_grpc_framer.http2_framer);

struct key_field key = {0};

struct value_field status = {0};
struct value_field grpc_status = {0};
struct value_field scheme = {0};
struct value_field authority = {0};
struct value_field path = {0};

#pragma unroll
for (size_t i = 0; i < 10; ++i)
{
if (i >= fields_len)
{
continue;
}
// Size of the golang hpack.HeaderField struct = 40
parse_header_field(&key.msg, &key.size, fields_ptr + i * 40);

// :status, grpc-status, :scheme, :path, :authority
if(key.size == 7 && key.msg[0] == ':' && key.msg[1] == 's' && key.msg[2] == 't' && key.msg[3] == 'a')
{
parse_header_field(&status.msg, &status.size, fields_ptr + i * 40 + 16);
break;
}
else if(key.size == 11 && key.msg[5] == 's' && key.msg[6] == 't' && key.msg[7] == 'a' && key.msg[8] == 't')
{
parse_header_field(&grpc_status.msg, &grpc_status.size, fields_ptr + i * 40 + 16);
break;
}
else if(key.size == 7 && key.msg[0] == ':' && key.msg[1] == 's' && key.msg[2] == 'c' && key.msg[3] == 'h')
{
parse_header_field(&scheme.msg, &scheme.size, fields_ptr + i * 40 + 16);
}
else if(key.size == 5 && key.msg[0] == ':' && key.msg[1] == 'p' && key.msg[2] == 'a' && key.msg[3] == 't')
{
parse_header_field(&path.msg, &path.size, fields_ptr + i * 40 + 16);
} else if(key.size == 10 && key.msg[0] == ':' && key.msg[1] == 'a' && key.msg[2] == 'u' && key.msg[3] == 't')
{
parse_header_field(&authority.msg, &authority.size, fields_ptr + i * 40 + 16);
}
}

int res;
res = bpf_val_to_ring(data, stream_id);
res = bpf_val_to_ring(data, fd);
res = bpf_val_to_ring(data, (uint32_t)end_stream);
res = bpf_val_to_ring_type(data, (unsigned long long)status.msg, PT_CHARBUF);
res = bpf_val_to_ring_type(data, (unsigned long long)grpc_status.msg, PT_CHARBUF);
res = bpf_val_to_ring_type(data, (unsigned long long)scheme.msg, PT_CHARBUF);
res = bpf_val_to_ring_type(data, (unsigned long long)authority.msg, PT_CHARBUF);
res = bpf_val_to_ring_type(data, (unsigned long long)path.msg, PT_CHARBUF);
return 0;
}

// server side: receive grpc-header
UP_FILLER(probe_http2_server_operate_headers){
struct pt_regs* regs = (struct pt_regs*) data->ctx;
const void *sp = (const void *)_READ(regs->sp);

void *http2_server_ptr = NULL;
bpf_probe_read(&http2_server_ptr, sizeof(http2_server_ptr), sp + 8);

void *frame_ptr;
bpf_probe_read(&frame_ptr, sizeof(void *), sp + 16);

struct go_interface conn_intf;
bpf_probe_read(&conn_intf, sizeof(conn_intf), http2_server_ptr + 32);

const int32_t fd = get_fd_from_conn_intf_core(conn_intf);

void *fields_ptr;
bpf_probe_read(&fields_ptr, sizeof(void *), frame_ptr + 8);

int64_t fields_len;
bpf_probe_read(&fields_len, sizeof(int64_t), frame_ptr + 8 + 8);

void *HeadersFrame_ptr;
bpf_probe_read(&HeadersFrame_ptr, sizeof(HeadersFrame_ptr), frame_ptr + 0);

uint32_t stream_id;
bpf_probe_read(&stream_id, sizeof(uint32_t), HeadersFrame_ptr + 8);

uint8_t flags;
bpf_probe_read(&flags, sizeof(uint8_t), HeadersFrame_ptr + 2);
const bool end_stream = flags & (0x1);

struct key_field key = {0};
struct value_field scheme = {0};
struct value_field authority = {0};
struct value_field path = {0};

#pragma unroll
for (size_t i = 0; i < 10; ++i)
{
if (i >= fields_len)
{
continue;
}
// Size of the golang hpack.HeaderField struct = 40
parse_header_field(&key.msg, &key.size, fields_ptr + i * 40);

// :scheme, :path, :authority
if(key.size == 7 && key.msg[0] == ':' && key.msg[1] == 's' && key.msg[2] == 'c' && key.msg[3] == 'h')
{
parse_header_field(&scheme.msg, &scheme.size, fields_ptr + i * 40 + 16);
}
else if(key.size == 5 && key.msg[0] == ':' && key.msg[1] == 'p' && key.msg[2] == 'a' && key.msg[3] == 't')
{
parse_header_field(&path.msg, &path.size, fields_ptr + i * 40 + 16);
}
else if(key.size == 10 && key.msg[0] == ':' && key.msg[1] == 'a' && key.msg[2] == 'u' && key.msg[3] == 't')
{
parse_header_field(&authority.msg, &authority.size, fields_ptr + i * 40 + 16);
}
}

int res;
res = bpf_val_to_ring(data, stream_id);
res = bpf_val_to_ring(data, fd);
res = bpf_val_to_ring(data, (uint32_t)end_stream);
res = bpf_val_to_ring_type(data, (unsigned long long)scheme.msg, PT_CHARBUF);
res = bpf_val_to_ring_type(data, (unsigned long long)authority.msg, PT_CHARBUF);
res = bpf_val_to_ring_type(data, (unsigned long long)path.msg, PT_CHARBUF);
return 0;
}

// client side: receive grpc-header
UP_FILLER(probe_http2_client_operate_headers){
struct pt_regs* regs = (struct pt_regs*) data->ctx;
const void *sp = (const void *)_READ(regs->sp);

void *http2_client_ptr = NULL;
bpf_probe_read(&http2_client_ptr, sizeof(http2_client_ptr), sp + 8);

void *frame_ptr;
bpf_probe_read(&frame_ptr, sizeof(void *), sp + 16);

struct go_interface conn_intf;
bpf_probe_read(&conn_intf, sizeof(conn_intf), http2_client_ptr + 64);

const int32_t fd = get_fd_from_conn_intf_core(conn_intf);

void *fields_ptr;
bpf_probe_read(&fields_ptr, sizeof(void *), frame_ptr + 8);

int64_t fields_len;
bpf_probe_read(&fields_len, sizeof(int64_t), frame_ptr + 8 + 8);

void *HeadersFrame_ptr;
bpf_probe_read(&HeadersFrame_ptr, sizeof(HeadersFrame_ptr), frame_ptr);

uint32_t stream_id;
bpf_probe_read(&stream_id, sizeof(uint32_t), HeadersFrame_ptr + 8);

uint8_t flags;
bpf_probe_read(&flags, sizeof(uint8_t), HeadersFrame_ptr + 2);
const bool end_stream = flags & (0x1);

struct key_field key = {0};
struct value_field status = {0};
struct value_field grpc_status = {0};

#pragma unroll
for (size_t i = 0; i < 10; ++i)
{
if (i >= fields_len)
{
continue;
}
// Size of the golang hpack.HeaderField struct = 40
parse_header_field(&key.msg, &key.size, fields_ptr + i * 40);

// :status, grpc-status
if(key.size == 7 && key.msg[0] == ':' && key.msg[1] == 's' && key.msg[2] == 't' && key.msg[3] == 'a')
{
parse_header_field(&status.msg, &status.size, fields_ptr + i * 40 + 16);
break;
}
else if(key.size == 11 && key.msg[5] == 's' && key.msg[6] == 't' && key.msg[7] == 'a' && key.msg[8] == 't')
{
parse_header_field(&grpc_status.msg, &grpc_status.size, fields_ptr + i * 40 + 16);
break;
}
}

int res;
res = bpf_val_to_ring(data, stream_id);
res = bpf_val_to_ring(data, fd);
res = bpf_val_to_ring(data, (uint32_t)end_stream);
res = bpf_val_to_ring_type(data, (unsigned long long)status.msg, PT_CHARBUF);
res = bpf_val_to_ring_type(data, (unsigned long long)grpc_status.msg, PT_CHARBUF);
return 0;
}

#endif
33 changes: 33 additions & 0 deletions driver/bpf/maps.h
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,39 @@ struct bpf_map_def __bpf_section("maps") cpu_focus_threads = {
.value_size = sizeof(u64),
.max_entries = 65535,
};

// grpc
#define MAX_HEADER_COUNT 32
#define HEADER_FIELD_STR_SIZE 40
struct go_grpc_framer_t
{
void *writer;
void *http2_framer;
};

struct go_interface
{
int64_t type;
void *ptr;
};

struct key_field {
uint32_t size;
char msg[40];
};

struct value_field {
uint32_t size;
char msg[40];
};

// This matches the golang string object memory layout. Used to help read golang string objects in BPF code.
struct gostring
{
const char *ptr;
int64_t len;
};

#endif // __KERNEL__

#endif
2 changes: 2 additions & 0 deletions driver/bpf/plumbing_helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ or GPL2.txt for full copies of the license.
#include "types.h"
#include "builtins.h"

// #define BPF_DEBUG

#define _READ(P) ({ typeof(P) _val; \
memset(&_val, 0, sizeof(_val)); \
bpf_probe_read(&_val, sizeof(_val), &P); \
Expand Down
Loading