Skip to content

Commit 6b69694

Browse files
authored
feat: re-implement go jsonrpc detector with net/rpc.Request (#335)
1 parent 8c997d2 commit 6b69694

File tree

11 files changed

+121
-310
lines changed

11 files changed

+121
-310
lines changed

bpf/gotracer/go_nethttp.c

Lines changed: 34 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,6 @@
2727
#include <gotracer/go_str.h>
2828
#include <gotracer/go_stream_key.h>
2929
#include <gotracer/hpack.h>
30-
#include <gotracer/protocol_jsonrpc.h>
31-
#include <gotracer/maps/jsonrpc_jump_table.h>
3230

3331
#include <logger/bpf_dbg.h>
3432

@@ -37,9 +35,7 @@
3735

3836
#include <pid/pid_helpers.h>
3937

40-
enum { k_tail_jsonrpc = 0 };
4138
static const char traceparent[] = "traceparent: ";
42-
static const char content_type[] = "content-type: ";
4339

4440
typedef struct http_client_data {
4541
s64 content_length;
@@ -70,13 +66,11 @@ typedef struct server_http_func_invocation {
7066
u64 content_length;
7167
u64 response_length;
7268
u64 status;
73-
u64 body_addr; // pointer to the body buffer
69+
u64 rpc_request_addr; // pointer to the jsonrpc Request
7470
tp_info_t tp;
75-
unsigned char content_type[HTTP_CONTENT_TYPE_MAX_LEN];
7671
u8 method[METHOD_MAX_LEN];
7772
u8 path[PATH_MAX_LEN];
78-
u8 json_content_type;
79-
u8 _pad[4];
73+
u8 _pad[5];
8074
} server_http_func_invocation_t;
8175

8276
struct {
@@ -98,18 +92,6 @@ static __always_inline unsigned char *temp_header_mem() {
9892
return bpf_map_lookup_elem(&temp_header_mem_store, &zero);
9993
}
10094

101-
struct {
102-
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
103-
__type(key, u32);
104-
__type(value, unsigned char[HTTP_BODY_MAX_LEN]);
105-
__uint(max_entries, 1);
106-
} temp_body_mem_store SEC(".maps");
107-
108-
static __always_inline unsigned char *temp_body_mem() {
109-
const u32 zero = 0;
110-
return bpf_map_lookup_elem(&temp_body_mem_store, &zero);
111-
}
112-
11395
/* HTTP Server */
11496

11597
// This instrumentation attaches uprobe to the following function:
@@ -151,15 +133,6 @@ int obi_uprobe_ServeHTTP(struct pt_regs *ctx) {
151133
// TODO: if context propagation is supported, overwrite the header value in the map with the
152134
// new span context and the same thread id.
153135

154-
// get content-type from readContinuedLineSlice
155-
if (header_inv && header_inv->content_type[0]) {
156-
bpf_dbg_printk("Found content type in ongoing request: %s", header_inv->content_type);
157-
if (is_json_content_type((void *)header_inv->content_type,
158-
sizeof(header_inv->content_type))) {
159-
invocation.json_content_type = 1;
160-
}
161-
}
162-
163136
// Get method from Request.Method
164137
if (!read_go_str("method",
165138
req,
@@ -311,13 +284,6 @@ static __always_inline void update_traceparent(server_http_func_invocation_t *in
311284
bpf_dbg_printk("Found traceparent in header %s", header_start);
312285
}
313286

314-
static __always_inline void update_content_type(server_http_func_invocation_t *inv,
315-
const unsigned char *header_start) {
316-
__builtin_memset(inv->content_type, 0, sizeof(inv->content_type));
317-
__builtin_memcpy(inv->content_type, header_start, sizeof(inv->content_type));
318-
bpf_dbg_printk("Found content-type in header %s", inv->content_type);
319-
}
320-
321287
static __always_inline void handle_traceparent_header(server_http_func_invocation_t *inv,
322288
go_addr_key_t *g_key,
323289
unsigned char *traceparent_start) {
@@ -330,18 +296,6 @@ static __always_inline void handle_traceparent_header(server_http_func_invocatio
330296
}
331297
}
332298

333-
static __always_inline void handle_content_type_header(server_http_func_invocation_t *inv,
334-
go_addr_key_t *g_key,
335-
unsigned char *content_type_start) {
336-
if (inv) {
337-
update_content_type(inv, content_type_start);
338-
} else {
339-
server_http_func_invocation_t minimal_inv = {};
340-
update_content_type(&minimal_inv, content_type_start);
341-
bpf_map_update_elem(&ongoing_http_server_requests, g_key, &minimal_inv, BPF_ANY);
342-
}
343-
}
344-
345299
// Matches the header in the buffer and returns a pointer to the value part of the header.
346300
static __always_inline unsigned char *match_header(
347301
const unsigned char *buf, u32 safe_len, const char *header, u32 header_len, u32 value_len) {
@@ -375,7 +329,6 @@ int obi_uprobe_readContinuedLineSliceReturns(struct pt_regs *ctx) {
375329
};
376330

377331
const u32 w3c_value_start = sizeof(traceparent) - 1;
378-
const u32 content_type_value_start = sizeof(content_type) - 1;
379332

380333
server_http_func_invocation_t *inv = bpf_map_lookup_elem(&ongoing_http_server_requests, &g_key);
381334

@@ -385,12 +338,6 @@ int obi_uprobe_readContinuedLineSliceReturns(struct pt_regs *ctx) {
385338
handle_traceparent_header(inv, &g_key, traceparent_start);
386339
}
387340

388-
unsigned char *content_type_start = match_header(
389-
temp, safe_len, content_type, content_type_value_start, HTTP_CONTENT_TYPE_MAX_LEN);
390-
if (content_type_start) {
391-
handle_content_type_header(inv, &g_key, content_type_start);
392-
}
393-
394341
return 0;
395342
}
396343

@@ -1299,99 +1246,68 @@ int obi_uprobe_netFdRead(struct pt_regs *ctx) {
12991246
return 0;
13001247
}
13011248

1302-
SEC("uprobe/bodyRead")
1303-
int obi_uprobe_bodyRead(struct pt_regs *ctx) {
1249+
SEC("uprobe/jsonrpcReadRequestHeader")
1250+
int obi_uprobe_jsonrpcReadRequestHeader(struct pt_regs *ctx) {
13041251
void *goroutine_addr = GOROUTINE_PTR(ctx);
1305-
bpf_dbg_printk("=== uprobe/proc body read goroutine === ");
1252+
bpf_dbg_printk("=== uprobe/proc jsonrpc read request header goroutine %lx === ",
1253+
goroutine_addr);
1254+
13061255
go_addr_key_t g_key = {};
13071256
go_addr_key_from_id(&g_key, goroutine_addr);
13081257

1309-
// Get the address of the slice struct (p)
1310-
u64 body_addr = (u64)GO_PARAM2(ctx);
1311-
13121258
server_http_func_invocation_t *invocation =
13131259
bpf_map_lookup_elem(&ongoing_http_server_requests, &g_key);
13141260
if (!invocation) {
13151261
return 0;
13161262
}
1317-
invocation->body_addr = body_addr;
1263+
const u64 rpc_request_addr = (u64)GO_PARAM2(ctx);
1264+
bpf_dbg_printk("rpc_request_addr %llx", rpc_request_addr);
1265+
invocation->rpc_request_addr = rpc_request_addr;
13181266

13191267
return 0;
13201268
}
13211269

1322-
SEC("uprobe/bodyReadRet")
1323-
int obi_uprobe_bodyReadReturn(struct pt_regs *ctx) {
1324-
bpf_dbg_printk("=== uprobe/proc body read returns goroutine === ");
1270+
SEC("uprobe/jsonrpcReadRequestHeaderRet")
1271+
int obi_uprobe_jsonrpcReadRequestHeaderReturns(struct pt_regs *ctx) {
13251272
void *goroutine_addr = GOROUTINE_PTR(ctx);
1273+
bpf_dbg_printk("=== uprobe/proc jsonrpc read request header return goroutine %lx === ",
1274+
goroutine_addr);
1275+
13261276
go_addr_key_t g_key = {};
13271277
go_addr_key_from_id(&g_key, goroutine_addr);
13281278

1329-
u64 n = (u64)GO_PARAM1(ctx);
1330-
bpf_dbg_printk("n is %llu", n);
1331-
13321279
server_http_func_invocation_t *invocation =
13331280
bpf_map_lookup_elem(&ongoing_http_server_requests, &g_key);
1334-
if (!invocation) {
1335-
return 0;
1336-
}
1337-
if (n <= 0 || !invocation->body_addr) {
1338-
return 0;
1339-
}
1340-
// json_content-type is set in invocation in ServeHTTP
1341-
if (invocation->json_content_type != 1) {
1342-
bpf_dbg_printk("content type is not json, skipping");
1343-
return 0;
1344-
}
13451281

1346-
unsigned char *body_buf = temp_body_mem();
1347-
if (!body_buf) {
1282+
if (!invocation || !invocation->rpc_request_addr) {
13481283
return 0;
13491284
}
13501285

1351-
const u32 safe_len = n > HTTP_BODY_MAX_LEN ? HTTP_BODY_MAX_LEN : n;
1352-
if (!read_go_str_n("http body", (void *)invocation->body_addr, n, body_buf, safe_len)) {
1353-
bpf_dbg_printk("failed to read body, n=%llu, body_addr=%llx", n, invocation->body_addr);
1354-
return 0;
1355-
}
1356-
// bpf_dbg_printk("body is %s", body_buf);
1357-
bpf_tail_call(ctx, &jsonrpc_jump_table, k_tail_jsonrpc);
1358-
return 0;
1359-
}
1286+
off_table_t *ot = get_offsets_table();
13601287

1361-
//k_tail_jsonrpc
1362-
SEC("uprobe/readJsonrpcMethod")
1363-
int obi_read_jsonrpc_method(struct pt_regs *ctx) {
1364-
bpf_dbg_printk("=== uprobe/proc read jsonrpc method === ");
1365-
void *goroutine_addr = GOROUTINE_PTR(ctx);
1366-
go_addr_key_t g_key = {};
1367-
go_addr_key_from_id(&g_key, goroutine_addr);
1288+
const u64 rpc_request_addr = invocation->rpc_request_addr;
13681289

1369-
server_http_func_invocation_t *invocation =
1370-
bpf_map_lookup_elem(&ongoing_http_server_requests, &g_key);
1371-
if (!invocation) {
1372-
bpf_dbg_printk("can't find invocation info for server call");
1290+
bpf_dbg_printk("rpc_request_addr %llx", rpc_request_addr);
1291+
1292+
const u64 method_len = peek_go_str_len(
1293+
"JSON-RPC method",
1294+
(void *)rpc_request_addr,
1295+
go_offset_of(ot, (go_offset){.v = _jsonrpc_request_header_service_method_pos}));
1296+
1297+
if (method_len == 0) {
13731298
return 0;
13741299
}
13751300

1376-
// tail call is guaranteed to run on the same CPU as its caller
1377-
// so we can shared the same buffer via a per-CPU map
1378-
unsigned char *body_buf = temp_body_mem();
1379-
if (!body_buf) {
1301+
if (!read_go_str("JSON-RPC method",
1302+
(void *)rpc_request_addr,
1303+
go_offset_of(ot, (go_offset){.v = _jsonrpc_request_header_service_method_pos}),
1304+
invocation->method,
1305+
METHOD_MAX_LEN)) {
1306+
bpf_dbg_printk("Failed to read JSON-RPC method from %llx", rpc_request_addr);
13801307
return 0;
13811308
}
1382-
if (is_jsonrpc2_body((const unsigned char *)body_buf, HTTP_BODY_MAX_LEN)) {
1383-
unsigned char method_buf[JSONRPC_METHOD_BUF_SIZE] = {};
1384-
u32 method_len = extract_jsonrpc2_method(
1385-
(const unsigned char *)body_buf, HTTP_BODY_MAX_LEN, method_buf, sizeof(method_buf));
1386-
if (method_len > 0) {
1387-
bpf_dbg_printk("JSON-RPC method: %s", method_buf);
1388-
read_go_str_n("JSON-RPC method",
1389-
(void *)method_buf,
1390-
method_len,
1391-
invocation->method,
1392-
sizeof(invocation->method));
1393-
}
1394-
}
1309+
bpf_dbg_printk("read jsonrpc method %s", invocation->method);
1310+
13951311
return 0;
13961312
}
13971313

bpf/gotracer/go_offsets.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ typedef enum {
7575
_tracer_delegate_pos,
7676
_tracer_attribute_opt_off,
7777
_error_string_off,
78+
// go jsonrpc
79+
_jsonrpc_request_header_service_method_pos,
7880
_last_go_offset,
7981
} go_offset_const;
8082

bpf/gotracer/go_str.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,12 @@ read_go_str(char *name, void *base_ptr, u8 offset, void *field, u64 max_size) {
5959

6060
return 1;
6161
}
62+
63+
static __always_inline u64 peek_go_str_len(const char *name, const void *base_ptr, u8 offset) {
64+
u64 len = 0;
65+
if (bpf_probe_read(&len, sizeof(len), (const void *)(base_ptr + (offset + 8))) != 0) {
66+
bpf_dbg_printk("can't read len for %s", name);
67+
return 0;
68+
}
69+
return len;
70+
}

bpf/gotracer/maps/jsonrpc_jump_table.h

Lines changed: 0 additions & 11 deletions
This file was deleted.

0 commit comments

Comments
 (0)