Skip to content

Commit

Permalink
[NPM] Add Enhanced TLS Tags (#31464)
Browse files Browse the repository at this point in the history
  • Loading branch information
akarpz authored Dec 23, 2024
1 parent f161600 commit d3df21f
Show file tree
Hide file tree
Showing 25 changed files with 1,299 additions and 123 deletions.
6 changes: 4 additions & 2 deletions pkg/network/ebpf/c/protocols/classification/defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ typedef enum {
// Each `protocol_t` entry is implicitly associated to a single
// `protocol_layer_t` value (see notes above).
//
//In order to determine which `protocol_layer_t` a `protocol_t` belongs to,
// In order to determine which `protocol_layer_t` a `protocol_t` belongs to,
// users can call `get_protocol_layer`
typedef enum {
LAYER_UNKNOWN,
Expand All @@ -103,7 +103,7 @@ typedef struct {
// `protocol_stack_t` is embedded in the `conn_stats_t` type, which is used
// across the whole NPM kernel code. If we added the 64-bit timestamp field
// directly to `protocol_stack_t`, we would go from 4 bytes to 12 bytes, which
// bloats the eBPF stack size of some NPM probes. Using the wrapper type
// bloats the eBPF stack size of some NPM probes. Using the wrapper type
// prevents that, because we pretty much only store the wrapper type in the
// connection_protocol map, but elsewhere in the code we're still using
// protocol_stack_t, so this is change is "transparent" to most of the code.
Expand All @@ -123,6 +123,8 @@ typedef enum {
CLASSIFICATION_GRPC_PROG,
__PROG_ENCRYPTION,
// Encryption classification programs go here
CLASSIFICATION_TLS_CLIENT_PROG,
CLASSIFICATION_TLS_SERVER_PROG,
CLASSIFICATION_PROG_MAX,
} classification_prog_t;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,15 +160,26 @@ __maybe_unused static __always_inline void protocol_classifier_entrypoint(struct

protocol_t app_layer_proto = get_protocol_from_stack(protocol_stack, LAYER_APPLICATION);

if ((app_layer_proto == PROTOCOL_UNKNOWN || app_layer_proto == PROTOCOL_POSTGRES) && is_tls(buffer, usm_ctx->buffer.size, skb_info.data_end)) {
tls_record_header_t tls_hdr = {0};

if ((app_layer_proto == PROTOCOL_UNKNOWN || app_layer_proto == PROTOCOL_POSTGRES) && is_tls(skb, skb_info.data_off, skb_info.data_end, &tls_hdr)) {
protocol_stack = get_or_create_protocol_stack(&usm_ctx->tuple);
if (!protocol_stack) {
return;
}
// TLS classification
update_protocol_information(usm_ctx, protocol_stack, PROTOCOL_TLS);
// The connection is TLS encrypted, thus we cannot classify the protocol
// using the socket filter and therefore we can bail out;
if (tls_hdr.content_type != TLS_HANDSHAKE) {
// We can't classify TLS encrypted traffic further, so return early
update_protocol_information(usm_ctx, protocol_stack, PROTOCOL_TLS);
return;
}

// Parse TLS handshake payload
tls_info_t *tags = get_or_create_tls_enhanced_tags(&usm_ctx->tuple);
if (tags) {
// The packet is a TLS handshake, so trigger tail calls to extract metadata from the payload
goto next_program;
}
return;
}

Expand Down Expand Up @@ -200,6 +211,58 @@ __maybe_unused static __always_inline void protocol_classifier_entrypoint(struct
classification_next_program(skb, usm_ctx);
}

__maybe_unused static __always_inline void protocol_classifier_entrypoint_tls_handshake_client(struct __sk_buff *skb) {
usm_context_t *usm_ctx = usm_context(skb);
if (!usm_ctx) {
return;
}
tls_info_t* tls_info = get_tls_enhanced_tags(&usm_ctx->tuple);
if (!tls_info) {
goto next_program;
}
__u32 offset = usm_ctx->skb_info.data_off + sizeof(tls_record_header_t);
__u32 data_end = usm_ctx->skb_info.data_end;
if (!is_tls_handshake_client_hello(skb, offset, usm_ctx->skb_info.data_end)) {
goto next_program;
}
if (!parse_client_hello(skb, offset, data_end, tls_info)) {
return;
}

next_program:
classification_next_program(skb, usm_ctx);
}

__maybe_unused static __always_inline void protocol_classifier_entrypoint_tls_handshake_server(struct __sk_buff *skb) {
usm_context_t *usm_ctx = usm_context(skb);
if (!usm_ctx) {
return;
}
tls_info_t* tls_info = get_tls_enhanced_tags(&usm_ctx->tuple);
if (!tls_info) {
goto next_program;
}
__u32 offset = usm_ctx->skb_info.data_off + sizeof(tls_record_header_t);
__u32 data_end = usm_ctx->skb_info.data_end;
if (!is_tls_handshake_server_hello(skb, offset, data_end)) {
goto next_program;
}
if (!parse_server_hello(skb, offset, data_end, tls_info)) {
return;
}

protocol_stack_t *protocol_stack = get_protocol_stack_if_exists(&usm_ctx->tuple);
if (!protocol_stack) {
return;
}
update_protocol_information(usm_ctx, protocol_stack, PROTOCOL_TLS);
// We can't classify TLS encrypted traffic further, so return early
return;

next_program:
classification_next_program(skb, usm_ctx);
}

__maybe_unused static __always_inline void protocol_classifier_entrypoint_queues(struct __sk_buff *skb) {
usm_context_t *usm_ctx = usm_context(skb);
if (!usm_ctx) {
Expand Down
31 changes: 4 additions & 27 deletions pkg/network/ebpf/c/protocols/classification/routing-helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,29 +18,7 @@ static __always_inline bool has_available_program(classification_prog_t current_
return true;
}

#pragma clang diagnostic push
// The following check is ignored because *currently* there are no API or
// Encryption classification programs registerd.
// Therefore the enum containing all BPF programs looks like the following:
//
// typedef enum {
// CLASSIFICATION_PROG_UNKNOWN = 0,
// __PROG_APPLICATION,
// APPLICATION_PROG_A
// APPLICATION_PROG_B
// APPLICATION_PROG_C
// ...
// __PROG_API,
// // No programs here
// __PROG_ENCRYPTION,
// // No programs here
// CLASSIFICATION_PROG_MAX,
// } classification_prog_t;
//
// Which means that the following conditionals will always evaluate to false:
// a) current_program > __PROG_API && current_program < __PROG_ENCRYPTION
// b) current_program > __PROG_ENCRYPTION && current_program < CLASSIFICATION_PROG_MAX
#pragma clang diagnostic ignored "-Wtautological-overlap-compare"
// get_current_program_layer returns the layer bit of the current program
static __always_inline u16 get_current_program_layer(classification_prog_t current_program) {
if (current_program > __PROG_APPLICATION && current_program < __PROG_API) {
return LAYER_APPLICATION_BIT;
Expand All @@ -56,20 +34,19 @@ static __always_inline u16 get_current_program_layer(classification_prog_t curre

return 0;
}
#pragma clang diagnostic pop

static __always_inline classification_prog_t next_layer_entrypoint(usm_context_t *usm_ctx) {
u16 to_skip = usm_ctx->routing_skip_layers;

if (!(to_skip&LAYER_ENCRYPTION_BIT)) {
return __PROG_ENCRYPTION+1;
}
if (!(to_skip&LAYER_APPLICATION_BIT)) {
return __PROG_APPLICATION+1;
}
if (!(to_skip&LAYER_API_BIT)) {
return __PROG_API+1;
}
if (!(to_skip&LAYER_ENCRYPTION_BIT)) {
return __PROG_ENCRYPTION+1;
}

return CLASSIFICATION_PROG_UNKNOWN;
}
Expand Down
Loading

0 comments on commit d3df21f

Please sign in to comment.