Skip to content
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
20 changes: 10 additions & 10 deletions ebpftracer/ebpf.go

Large diffs are not rendered by default.

57 changes: 39 additions & 18 deletions ebpftracer/ebpf/l7/http.c
Original file line number Diff line number Diff line change
@@ -1,33 +1,54 @@

// is_http_request reports whether buf starts an HTTP/1.x request. A request
// line can be longer than the bytes read here, so " HTTP/" may lie past them:
// a target in origin form (a path) or absolute form (a proxy's http:// URL)
// is enough. Requiring the version within the buffer dropped every request
// with a target longer than about 50 bytes that opened its connection. Other
// targets (CONNECT host:port, OPTIONS *) are confirmed by the version, as
// before, which is what keeps Redis inline commands and binary data out.
static __always_inline
int is_http_request(char *buf) {
char b[64]; // Increased buffer to 64 bytes
if (bpf_probe_read_str(&b, sizeof(b), (void *)buf) < 8) { // Still need at least "GET / HTTP/1.1"
// Zeroed, as bpf_probe_read_str leaves the bytes past the string as the
// stack had them: a previous request's " HTTP/" there passed the scan
// below. Eight stores, where bounding the scan by the length read added a
// branch per step and half again to the verifier's work.
char b[64] = {};
if (bpf_probe_read_str(&b, sizeof(b), (void *)buf) < 8) {
return 0;
}

// First, check for a valid HTTP method. This is a strong and fast first filter.
if (!((b[0] == 'G' && b[1] == 'E' && b[2] == 'T' && b[3] == ' ') ||
(b[0] == 'P' && b[1] == 'O' && b[2] == 'S' && b[3] == 'T' && b[4] == ' ') ||
(b[0] == 'H' && b[1] == 'E' && b[2] == 'A' && b[3] == 'D' && b[4] == ' ') ||
(b[0] == 'P' && b[1] == 'U' && b[2] == 'T' && b[3] == ' ') ||
(b[0] == 'D' && b[1] == 'E' && b[2] == 'L' && b[3] == 'E' && b[4] == 'T' && b[5] == 'E' && b[6] == ' ') ||
(b[0] == 'C' && b[1] == 'O' && b[2] == 'N' && b[3] == 'N' && b[4] == 'E' && b[5] == 'C' && b[6] == 'T' && b[7] == ' ') ||
(b[0] == 'O' && b[1] == 'P' && b[2] == 'T' && b[3] == 'I' && b[4] == 'O' && b[5] == 'N' && b[6] == 'S' && b[7] == ' ') ||
(b[0] == 'P' && b[1] == 'A' && b[2] == 'T' && b[3] == 'C' && b[4] == 'H' && b[5] == ' '))) {
// The first bytes of the request target, after the method and its space.
// Constant offsets per method keep the reads below fixed for the verifier.
char t0, t1, t2, t3;
if (b[0] == 'G' && b[1] == 'E' && b[2] == 'T' && b[3] == ' ') {
t0 = b[4]; t1 = b[5]; t2 = b[6]; t3 = b[7];
} else if (b[0] == 'P' && b[1] == 'U' && b[2] == 'T' && b[3] == ' ') {
t0 = b[4]; t1 = b[5]; t2 = b[6]; t3 = b[7];
} else if ((b[0] == 'P' && b[1] == 'O' && b[2] == 'S' && b[3] == 'T' && b[4] == ' ') ||
(b[0] == 'H' && b[1] == 'E' && b[2] == 'A' && b[3] == 'D' && b[4] == ' ')) {
t0 = b[5]; t1 = b[6]; t2 = b[7]; t3 = b[8];
} else if (b[0] == 'P' && b[1] == 'A' && b[2] == 'T' && b[3] == 'C' && b[4] == 'H' && b[5] == ' ') {
t0 = b[6]; t1 = b[7]; t2 = b[8]; t3 = b[9];
} else if (b[0] == 'D' && b[1] == 'E' && b[2] == 'L' && b[3] == 'E' && b[4] == 'T' && b[5] == 'E' && b[6] == ' ') {
t0 = b[7]; t1 = b[8]; t2 = b[9]; t3 = b[10];
} else if ((b[0] == 'C' && b[1] == 'O' && b[2] == 'N' && b[3] == 'N' && b[4] == 'E' && b[5] == 'C' && b[6] == 'T' && b[7] == ' ') ||
(b[0] == 'O' && b[1] == 'P' && b[2] == 'T' && b[3] == 'I' && b[4] == 'O' && b[5] == 'N' && b[6] == 'S' && b[7] == ' ')) {
t0 = b[8]; t1 = b[9]; t2 = b[10]; t3 = b[11];
} else {
return 0;
}

// Now, scan the larger buffer for " HTTP/" to distinguish from Redis, etc.
// This confirms it's a true HTTP request.
if (t0 == '/') {
return 1;
}
if (t0 == 'h' && t1 == 't' && t2 == 't' && t3 == 'p') {
return 1;
}
#pragma unroll
for (int i = 3; i < sizeof(b) - 7; i++) {
if (b[i] == ' ' && b[i+1] == 'H' && b[i+2] == 'T' && b[i+3] == 'T' && b[i+4] == 'P' && b[i+5] == '/') {
return 1; // Confirmed HTTP request.
return 1;
}
}

return 0; // Method looked like HTTP, but protocol string was not found.
return 0;
}

static __always_inline
Expand Down
Loading