Skip to content

Commit 478b3b0

Browse files
authored
Merge pull request #329 from coroot/fix_319
fix: strengthen eBPF ClickHouse protocol detection
2 parents 0ea5fee + b53b38d commit 478b3b0

3 files changed

Lines changed: 107 additions & 26 deletions

File tree

ebpftracer/ebpf.go

Lines changed: 10 additions & 10 deletions
Large diffs are not rendered by default.

ebpftracer/ebpf/l7/clickhouse.c

Lines changed: 96 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,38 +9,119 @@
99
#define CLICKHOUSE_SERVER_CODE_EXCEPTION 2
1010
#define CLICKHOUSE_SERVER_CODE_END_OF_STREAM 5
1111

12+
#define CLICKHOUSE_COMPRESSION_NONE 0x02
13+
#define CLICKHOUSE_COMPRESSION_LZ4 0x82
14+
#define CLICKHOUSE_COMPRESSION_ZSTD 0x90
15+
16+
#define CLICKHOUSE_MIN_QUERY_SIZE 40
17+
#define CLICKHOUSE_MAX_USER_SIZE 63
18+
#define CLICKHOUSE_MAX_ADDRESS_SIZE 48
19+
20+
static __always_inline
21+
int is_clickhouse_uuid(char *buf) {
22+
__u8 u[CLICKHOUSE_QUERY_ID_SIZE];
23+
bpf_read(buf, u);
24+
if (u[8] != '-' || u[13] != '-' || u[18] != '-' || u[23] != '-') {
25+
return 0;
26+
}
27+
return 1;
28+
}
29+
1230
static __always_inline
1331
int is_clickhouse_query(char *buf, __u64 buf_size) {
14-
__u8 b[CLICKHOUSE_QUERY_ID_SIZE+3];
32+
if (buf_size < CLICKHOUSE_MIN_QUERY_SIZE) {
33+
return 0;
34+
}
35+
__u8 b[2];
1536
bpf_read(buf, b);
1637
if (b[0] != CLICKHOUSE_CLIENT_CODE_QUERY) {
1738
return 0;
1839
}
19-
int offset = 0;
20-
if (b[1] == 0) {
21-
offset = 2;
22-
} else if (b[1] == CLICKHOUSE_QUERY_ID_SIZE) {
23-
offset = 2 + CLICKHOUSE_QUERY_ID_SIZE;
24-
} else {
40+
__u64 offset = 2;
41+
if (b[1] == CLICKHOUSE_QUERY_ID_SIZE) {
42+
if (!is_clickhouse_uuid(buf+2)) {
43+
return 0;
44+
}
45+
offset += CLICKHOUSE_QUERY_ID_SIZE;
46+
} else if (b[1] != 0) {
47+
return 0;
48+
}
49+
__u8 kind = 0;
50+
bpf_read(buf+offset, kind);
51+
if (kind != CLICKHOUSE_QUERY_KIND_INITIAL && kind != CLICKHOUSE_QUERY_KIND_SECONDARY) {
52+
return 0;
53+
}
54+
offset += 1;
55+
__u8 len = 0;
56+
bpf_read(buf+offset, len); // initial_user
57+
if (len > CLICKHOUSE_MAX_USER_SIZE) {
2558
return 0;
2659
}
27-
if (b[offset] != CLICKHOUSE_QUERY_KIND_INITIAL && b[offset] != CLICKHOUSE_QUERY_KIND_SECONDARY) {
60+
offset += 1 + len;
61+
bpf_read(buf+offset, len); // initial_query_id
62+
if (len == CLICKHOUSE_QUERY_ID_SIZE) {
63+
if (!is_clickhouse_uuid(buf+offset+1)) {
64+
return 0;
65+
}
66+
} else if (len != 0) {
67+
return 0;
68+
}
69+
offset += 1 + len;
70+
bpf_read(buf+offset, len); // initial_address
71+
if (len > CLICKHOUSE_MAX_ADDRESS_SIZE) {
72+
return 0;
73+
}
74+
if (len > 0) {
75+
__u8 c = 0;
76+
bpf_read(buf+offset+1, c);
77+
if (!((c >= '0' && c <= '9') || c == '[' || c == ':')) {
78+
return 0;
79+
}
80+
}
81+
offset += 1 + len + 8; // interface follows the 8-byte initial_query_start_time_microseconds
82+
if (offset >= buf_size) {
83+
return 0;
84+
}
85+
__u8 iface = 0;
86+
bpf_read(buf+offset, iface); // TCP, HTTP, GRPC, MYSQL, POSTGRESQL, LOCAL, TCP_INTERSERVER, PROMETHEUS
87+
if (iface < 1 || iface > 8) {
2888
return 0;
2989
}
3090
return 1;
3191
}
3292

3393
static __always_inline
34-
int is_clickhouse_response(char *buf, __s32 *status) {
35-
__u8 code = 0;
36-
bpf_read(buf, code);
37-
if (code == CLICKHOUSE_SERVER_CODE_DATA || code == CLICKHOUSE_SERVER_CODE_END_OF_STREAM) {
38-
*status = STATUS_OK;
39-
return 1;
94+
int is_clickhouse_response(char *buf, __u64 buf_size, __s32 *status) {
95+
__u8 b[3];
96+
bpf_read(buf, b);
97+
if (b[0] == CLICKHOUSE_SERVER_CODE_DATA) {
98+
if (b[1] != 0) { // temporary table name is always empty
99+
return 0;
100+
}
101+
if (b[2] == 1) { // uncompressed block: BlockInfo field number 1
102+
*status = STATUS_OK;
103+
return 1;
104+
}
105+
__u8 method = 0;
106+
bpf_read(buf+2+16, method); // compressed block: compression method follows the 16-byte checksum
107+
if (method == CLICKHOUSE_COMPRESSION_LZ4 || method == CLICKHOUSE_COMPRESSION_ZSTD || method == CLICKHOUSE_COMPRESSION_NONE) {
108+
*status = STATUS_OK;
109+
return 1;
110+
}
111+
return 0;
40112
}
41-
if (code == CLICKHOUSE_SERVER_CODE_EXCEPTION) {
113+
if (b[0] == CLICKHOUSE_SERVER_CODE_EXCEPTION) {
114+
__s32 code = 0;
115+
bpf_read(buf+1, code);
116+
if (code <= 0 || code > 4096) {
117+
return 0;
118+
}
42119
*status = STATUS_FAILED;
43120
return 1;
44121
}
122+
if (b[0] == CLICKHOUSE_SERVER_CODE_END_OF_STREAM && buf_size == 1) {
123+
*status = STATUS_OK;
124+
return 1;
125+
}
45126
return 0;
46127
}

ebpftracer/ebpf/l7/l7.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -616,7 +616,7 @@ int handle_response(void *ctx, struct connection_id cid, struct connection *conn
616616
} else if (e->protocol == PROTOCOL_KAFKA) {
617617
response = is_kafka_response(payload, req->request_id);
618618
} else if (e->protocol == PROTOCOL_CLICKHOUSE) {
619-
response = is_clickhouse_response(payload, &e->status);
619+
response = is_clickhouse_response(payload, ret, &e->status);
620620
if (!response) {
621621
return 0; // keeping the query in the map
622622
}

0 commit comments

Comments
 (0)