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

(Experimental) Trace context propagation via HTTP headers #1291

Merged
merged 15 commits into from
Nov 2, 2024

Conversation

rafaelroquetto
Copy link
Contributor

@rafaelroquetto rafaelroquetto commented Oct 30, 2024

Inject Traceparent into HTTP headers for trace context propagation. This is done using a pair of tc programs: the egress program monitors connection metadata and, upon encountering a HTTP request, it will attempt to inject the Traceparent: string directly into the packet. This entails extending the packet, writing the traceparent, and recalculating the relevant checksums. The engress program stores the new expected sequence number in a map. It ensures packets that follow are correctly adjusted.

The egress program, conversely, adjusts the arriving sequence number back to what is expected by the higher-level kernel layers. If left unchanged, the kernel will drop the packet and will trigger a TCP retransmission.

For the handling of closing TCP connections, please refer to the following diagram to assist understanding the code:
image

@rafaelroquetto rafaelroquetto added the do-not-merge WIP or something that musn't be merged label Oct 30, 2024

struct seq_offset_map {
__uint(type, BPF_MAP_TYPE_HASH);
__type(key, __u32);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is currently only indexing on the ephemeral port - I reckon we require something a bit smarter here.

bpf/tc_http_tp.c Outdated
Comment on lines 165 to 180
buf = check_pkt_access(buf, EXTEND_SIZE, end);

if (!buf)
return;

*buf++ = 'T';
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This keeps the verifier happy, both in terms of register offset validation, and in terms of keeping the program complexity at bay.

return seq ? *seq : 0;
}

static __always_inline int is_http_request(struct __sk_buff *ctx) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had to merge the logic from is_http here, again, for complexity reasons (in this case, doing without unnecessary branching and storing the packet_type).

bpf/tc_http_tp.c Outdated
}

static __always_inline unsigned char *
memchar(unsigned char *haystack, char needle, unsigned char *end, __u32 size) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

perhaps the inner checks here can be avoided if check_pkt_access() is used

Copy link

codecov bot commented Oct 30, 2024

Codecov Report

Attention: Patch coverage is 62.80193% with 77 lines in your changes missing coverage. Please review.

Project coverage is 81.07%. Comparing base (1f75eee) to head (faff19a).
Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
pkg/internal/ebpf/httptracer/httptracer.go 61.80% 61 Missing and 15 partials ⚠️
pkg/beyla/os.go 0.00% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1291      +/-   ##
==========================================
- Coverage   81.25%   81.07%   -0.18%     
==========================================
  Files         142      143       +1     
  Lines       14295    14499     +204     
==========================================
+ Hits        11615    11755     +140     
- Misses       2116     2170      +54     
- Partials      564      574      +10     
Flag Coverage Δ
integration-test 59.06% <61.35%> (+0.27%) ⬆️
k8s-integration-test 59.65% <62.31%> (+0.11%) ⬆️
oats-test 34.53% <1.44%> (-0.49%) ⬇️
unittests 51.64% <0.00%> (-0.76%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@rafaelroquetto rafaelroquetto force-pushed the http_ctx branch 8 times, most recently from 539b6ee to 6baec39 Compare November 1, 2024 19:43
@rafaelroquetto rafaelroquetto marked this pull request as ready for review November 1, 2024 21:21
@rafaelroquetto rafaelroquetto changed the title WIP: Trace context propagation via HTTP headers (Experimental) Trace context propagation via HTTP headers Nov 1, 2024
@rafaelroquetto rafaelroquetto removed the do-not-merge WIP or something that musn't be merged label Nov 1, 2024
Copy link
Contributor

@grcevski grcevski left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Looks great, amazing work. I left a few comments about code reuse, they can be done in a follow-up.

//go:generate $BPF2GO -cc $BPF_CLANG -cflags $BPF_CFLAGS -target amd64,arm64 bpf ../../../../bpf/tc_http_tp.c -- -I../../../../bpf/headers
//go:generate $BPF2GO -cc $BPF_CLANG -cflags $BPF_CFLAGS -target amd64,arm64 bpf_debug ../../../../bpf/tc_http_tp.c -- -I../../../../bpf/headers -DBPF_DEBUG

type Tracer struct {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can common some code here with the tctracer. Maybe we can make those qdisc routines helpers that can be reused?

return 0;
}

return (req_buf[0] == 'G' && req_buf[1] == 'E' && req_buf[2] == 'T' && req_buf[3] == ' ' &&
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense the code duplication, not critical, but perhaps we can just extract this code into a macro or __always_inline to avoid having to change this in two places, no that we ever will :).

// we've only received ACK, but no FIN, wait for it
http_ctx->state = FIN_WAIT_2;
}
} else if (http_ctx->state == CLOSING && tcp->ack) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can turn these into OR statements to save some instructions, that way delete_http_ctx will get inlined once only.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excellent idea, will also do it on a follow up PR

@grcevski
Copy link
Contributor

grcevski commented Nov 2, 2024

Any way we can keep that image you have here in a doc, perhaps a markdown file in the same folder as the bpfs?

@rafaelroquetto rafaelroquetto merged commit 426bfcc into main Nov 2, 2024
15 checks passed
@rafaelroquetto rafaelroquetto deleted the http_ctx branch November 2, 2024 00:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants