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

Add support for dynamic gRPC tables #1530

Merged
merged 4 commits into from
Jan 15, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
1 change: 1 addition & 0 deletions bpf/http_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ typedef struct http2_grpc_request {
// with other instrumented processes
pid_info pid;
u8 ssl;
u8 new_conn;
tp_info_t tp;
} http2_grpc_request_t;

Expand Down
9 changes: 6 additions & 3 deletions bpf/k_tracer_defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,14 @@ static __always_inline void handle_buf_with_args(void *ctx, call_protocol_args_t
bpf_tail_call(ctx, &jump_table, k_tail_protocol_http);
} else if (is_http2_or_grpc(args->small_buf, MIN_HTTP2_SIZE)) {
bpf_dbg_printk("Found HTTP2 or gRPC connection");
u8 is_ssl = args->ssl;
bpf_map_update_elem(&ongoing_http2_connections, &args->pid_conn, &is_ssl, BPF_ANY);
u8 flags = http2_conn_flag_new;
if (args->ssl) {
flags |= http2_conn_flag_ssl;
}
bpf_map_update_elem(&ongoing_http2_connections, &args->pid_conn, &flags, BPF_ANY);
} else {
u8 *h2g = bpf_map_lookup_elem(&ongoing_http2_connections, &args->pid_conn);
if (h2g && *h2g == args->ssl) {
if (h2g && (http2_flag_ssl(*h2g) == args->ssl)) {
bpf_tail_call(ctx, &jump_table, k_tail_protocol_http2);
} else { // large request tracking
http_info_t *info = bpf_map_lookup_elem(&ongoing_http, &args->pid_conn);
Expand Down
20 changes: 19 additions & 1 deletion bpf/protocol_http2.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,13 @@
#include "protocol_common.h"
#include "ringbuf.h"

// These are bit flags, if you add any use power of 2 values
enum { http2_conn_flag_ssl = WITH_SSL, http2_conn_flag_new = 0x2 };

struct {
__uint(type, BPF_MAP_TYPE_LRU_HASH);
__type(key, pid_connection_info_t);
__type(value, u8); // ssl or not
__type(value, u8); // flags
__uint(max_entries, MAX_CONCURRENT_REQUESTS);
} ongoing_http2_connections SEC(".maps");

Expand Down Expand Up @@ -62,6 +65,14 @@ static __always_inline grpc_frames_ctx_t *grpc_ctx() {
return bpf_map_lookup_elem(&grpc_frames_ctx_mem, &zero);
}

static __always_inline u8 http2_flag_ssl(u8 flags) {
return flags & http2_conn_flag_ssl;
}

static __always_inline u8 http2_flag_new(u8 flags) {
return flags & http2_conn_flag_new;
}

static __always_inline http2_grpc_request_t *empty_http2_info() {
int zero = 0;
http2_grpc_request_t *value = bpf_map_lookup_elem(&http2_info_mem, &zero);
Expand Down Expand Up @@ -98,6 +109,13 @@ static __always_inline void http2_grpc_start(
h2g_info->pid = meta->pid;
h2g_info->type = meta->type;
}

h2g_info->new_conn = 0;
u8 *h2g = bpf_map_lookup_elem(&ongoing_http2_connections, &s_key->pid_conn);
if (h2g && http2_flag_new(*h2g)) {
h2g_info->new_conn = 1;
}

fixup_connection_info(
&h2g_info->conn_info, h2g_info->type == EVENT_HTTP_CLIENT, orig_dport);
bpf_probe_read(h2g_info->data, KPROBES_HTTP2_BUF_SIZE, u_buf);
Expand Down
20 changes: 15 additions & 5 deletions pkg/internal/ebpf/bhpack/hpack.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,17 +95,21 @@ type Decoder struct {
// to fully parse before. Unlike buf, we own this data.
saveBuf bytes.Buffer

firstField bool // processing the first field of the header block
firstField bool // processing the first field of the header block
lastGoodIndex uint64
failedToIndex bool
}

// NewDecoder returns a new decoder with the provided maximum dynamic
// table size. The emitFunc will be called for each valid field
// parsed, in the same goroutine as calls to Write, before Write returns.
func NewDecoder(maxDynamicTableSize uint32, emitFunc func(f HeaderField)) *Decoder {
d := &Decoder{
emit: emitFunc,
emitEnabled: true,
firstField: true,
emit: emitFunc,
emitEnabled: true,
firstField: true,
failedToIndex: false,
lastGoodIndex: 0,
}
d.dynTab.table.init()
d.dynTab.allowedMaxSize = maxDynamicTableSize
Expand Down Expand Up @@ -345,9 +349,15 @@ func (d *Decoder) parseFieldIndexed() error {
}
hf, ok := d.at(idx)
d.buf = buf
if !ok {
// If we've failed once to find an index, don't allow us to find
// a value for index that's greater than the last successful one
if !ok || (d.failedToIndex && idx > d.lastGoodIndex) {
Copy link
Contributor

Choose a reason for hiding this comment

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

did you mean !d.failedToIndex here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

it should be d.failedToIndex, but I caught a bug with that. I have a new version which changes how this is done now. All I block is adding new entries to the dynamic table. I'll push an update soon once I have integration tests

d.failedToIndex = true
return d.callEmit(HeaderField{Name: "<BAD INDEX>", Value: ""})
}
if idx > d.lastGoodIndex {
d.lastGoodIndex = idx
}
return d.callEmit(HeaderField{Name: hf.Name, Value: hf.Value})
}

Expand Down
7 changes: 4 additions & 3 deletions pkg/internal/ebpf/common/bpf_arm64_bpfel.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions pkg/internal/ebpf/common/bpf_arm64_bpfel.o
Git LFS file not shown
7 changes: 4 additions & 3 deletions pkg/internal/ebpf/common/bpf_x86_bpfel.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions pkg/internal/ebpf/common/bpf_x86_bpfel.o
Git LFS file not shown
38 changes: 25 additions & 13 deletions pkg/internal/ebpf/common/http2grpc_transform.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ const (
GRPC
)

const initialHeaderTableSize = 4096

type h2Connection struct {
hdec *bhpack.Decoder
hdecRet *bhpack.Decoder
Expand All @@ -45,13 +47,18 @@ func byteFramer(data []uint8) *http2.Framer {
return fr
}

func getOrInitH2Conn(conn *BPFConnInfo) *h2Connection {
func getOrInitH2Conn(conn *BPFConnInfo, newConn bool) *h2Connection {
v, ok := activeGRPCConnections.Get(*conn)

dynamicTableSize := initialHeaderTableSize
if !newConn {
dynamicTableSize = 0
}

if !ok {
h := h2Connection{
hdec: bhpack.NewDecoder(0, nil),
hdecRet: bhpack.NewDecoder(0, nil),
hdec: bhpack.NewDecoder(uint32(dynamicTableSize), nil),
hdecRet: bhpack.NewDecoder(uint32(dynamicTableSize), nil),
protocol: HTTP2,
}
activeGRPCConnections.Add(*conn, h)
Expand All @@ -64,8 +71,8 @@ func getOrInitH2Conn(conn *BPFConnInfo) *h2Connection {
return &v
}

func protocolIsGRPC(conn *BPFConnInfo) {
h2c := getOrInitH2Conn(conn)
func protocolIsGRPC(conn *BPFConnInfo, newConn bool) {
h2c := getOrInitH2Conn(conn, newConn)
if h2c != nil {
h2c.protocol = GRPC
}
Expand Down Expand Up @@ -102,8 +109,8 @@ func knownFrameKeys(fr *http2.Framer, hf *http2.HeadersFrame) bool {
return known
}

func readMetaFrame(conn *BPFConnInfo, fr *http2.Framer, hf *http2.HeadersFrame) (string, string, string, bool) {
h2c := getOrInitH2Conn(conn)
func readMetaFrame(conn *BPFConnInfo, newConn bool, fr *http2.Framer, hf *http2.HeadersFrame) (string, string, string, bool) {
h2c := getOrInitH2Conn(conn, newConn)

ok := false
method := ""
Expand All @@ -126,7 +133,7 @@ func readMetaFrame(conn *BPFConnInfo, fr *http2.Framer, hf *http2.HeadersFrame)
case "content-type":
contentType = strings.ToLower(hf.Value)
if contentType == "application/grpc" {
protocolIsGRPC(conn)
protocolIsGRPC(conn, newConn)
}
ok = true
}
Expand Down Expand Up @@ -162,8 +169,8 @@ func http2grpcStatus(status int) int {
return 2 // Unknown
}

func readRetMetaFrame(conn *BPFConnInfo, fr *http2.Framer, hf *http2.HeadersFrame) (int, bool, bool) {
h2c := getOrInitH2Conn(conn)
func readRetMetaFrame(conn *BPFConnInfo, newConn bool, fr *http2.Framer, hf *http2.HeadersFrame) (int, bool, bool) {
h2c := getOrInitH2Conn(conn, newConn)

ok := false
status := 0
Expand All @@ -184,7 +191,7 @@ func readRetMetaFrame(conn *BPFConnInfo, fr *http2.Framer, hf *http2.HeadersFram
ok = true
case "grpc-status":
status, _ = strconv.Atoi(hf.Value)
protocolIsGRPC(conn)
protocolIsGRPC(conn, newConn)
grpc = true
ok = true
}
Expand Down Expand Up @@ -276,6 +283,11 @@ func http2FromBuffers(event *BPFHTTP2Info) (request.Span, bool, error) {
status := 0
eventType := HTTP2

newConn := true
if event.NewConn == 0 {
newConn = false
}

for {
f, err := framer.ReadFrame()

Expand All @@ -285,7 +297,7 @@ func http2FromBuffers(event *BPFHTTP2Info) (request.Span, bool, error) {

if ff, ok := f.(*http2.HeadersFrame); ok {
rok := false
method, path, contentType, ok := readMetaFrame((*BPFConnInfo)(&event.ConnInfo), framer, ff)
method, path, contentType, ok := readMetaFrame((*BPFConnInfo)(&event.ConnInfo), newConn, framer, ff)

if path == "" {
path = "*"
Expand All @@ -301,7 +313,7 @@ func http2FromBuffers(event *BPFHTTP2Info) (request.Span, bool, error) {
}

if ff, ok := retF.(*http2.HeadersFrame); ok {
status, grpcInStatus, rok = readRetMetaFrame((*BPFConnInfo)(&event.ConnInfo), retFramer, ff)
status, grpcInStatus, rok = readRetMetaFrame((*BPFConnInfo)(&event.ConnInfo), newConn, retFramer, ff)
break
}
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/internal/ebpf/common/http2grpc_transform_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ func TestHTTP2Parsing(t *testing.T) {

if ff, ok := f.(*http2.HeadersFrame); ok {
connInfo := BPFConnInfo{}
method, path, contentType, _ := readMetaFrame(&connInfo, framer, ff)
method, path, contentType, _ := readMetaFrame(&connInfo, false, framer, ff)
assert.Equal(t, method, tt.method)
assert.Equal(t, path, tt.path)
assert.Equal(t, contentType, tt.contentType)
Expand Down
7 changes: 4 additions & 3 deletions pkg/internal/ebpf/generictracer/bpf_arm64_bpfel.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions pkg/internal/ebpf/generictracer/bpf_arm64_bpfel.o
Git LFS file not shown
7 changes: 4 additions & 3 deletions pkg/internal/ebpf/generictracer/bpf_debug_arm64_bpfel.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions pkg/internal/ebpf/generictracer/bpf_debug_arm64_bpfel.o
Git LFS file not shown
7 changes: 4 additions & 3 deletions pkg/internal/ebpf/generictracer/bpf_debug_x86_bpfel.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions pkg/internal/ebpf/generictracer/bpf_debug_x86_bpfel.o
Git LFS file not shown
7 changes: 4 additions & 3 deletions pkg/internal/ebpf/generictracer/bpf_tp_arm64_bpfel.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions pkg/internal/ebpf/generictracer/bpf_tp_arm64_bpfel.o
Git LFS file not shown
7 changes: 4 additions & 3 deletions pkg/internal/ebpf/generictracer/bpf_tp_debug_arm64_bpfel.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions pkg/internal/ebpf/generictracer/bpf_tp_debug_arm64_bpfel.o
Git LFS file not shown
7 changes: 4 additions & 3 deletions pkg/internal/ebpf/generictracer/bpf_tp_debug_x86_bpfel.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions pkg/internal/ebpf/generictracer/bpf_tp_debug_x86_bpfel.o
Git LFS file not shown
Loading
Loading