-
Notifications
You must be signed in to change notification settings - Fork 269
Allow sockops BPF programs to query the WFP flow_id #4763
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -579,8 +579,8 @@ _ebpf_bind_context_create( | |
| ebpf_result_t retval; | ||
| *context = nullptr; | ||
| bind_md_t* bind_context = nullptr; | ||
| bind_context_header_t* bind_context_header = | ||
| reinterpret_cast<bind_context_header_t*>(ebpf_allocate_with_tag(sizeof(bind_context_header_t), EBPF_POOL_TAG_DEFAULT)); | ||
| bind_context_header_t* bind_context_header = reinterpret_cast<bind_context_header_t*>( | ||
| ebpf_allocate_with_tag(sizeof(bind_context_header_t), EBPF_POOL_TAG_DEFAULT)); | ||
| if (bind_context_header == nullptr) { | ||
| retval = EBPF_NO_MEMORY; | ||
| goto Done; | ||
|
|
@@ -714,8 +714,8 @@ _ebpf_sock_addr_context_create( | |
| *context = nullptr; | ||
|
|
||
| bpf_sock_addr_t* sock_addr_context = nullptr; | ||
| sock_addr_context_header_t* sock_addr_context_header = | ||
| reinterpret_cast<sock_addr_context_header_t*>(ebpf_allocate_with_tag(sizeof(sock_addr_context_header_t), EBPF_POOL_TAG_DEFAULT)); | ||
| sock_addr_context_header_t* sock_addr_context_header = reinterpret_cast<sock_addr_context_header_t*>( | ||
| ebpf_allocate_with_tag(sizeof(sock_addr_context_header_t), EBPF_POOL_TAG_DEFAULT)); | ||
| if (sock_addr_context_header == nullptr) { | ||
| retval = EBPF_NO_MEMORY; | ||
| goto Done; | ||
|
|
@@ -818,13 +818,27 @@ _ebpf_sock_ops_get_current_pid_tgid( | |
| return 0; | ||
| } | ||
|
|
||
| static uint64_t | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why is this needed in addition to the one in netebpfext/net_ebpf_ext_sock_ops.c ?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is for the mock layer that allows BPF programs to be run in unit_tests.exe, entirely in user mode with out netebpfext.sys. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see, thanks |
||
| _ebpf_sock_ops_get_flow_id(_In_ const bpf_sock_ops_t* ctx) | ||
| { | ||
| UNREFERENCED_PARAMETER(ctx); | ||
| return 12345; // Mock flow ID for testing. | ||
| } | ||
|
|
||
| static const void* _ebpf_sock_ops_global_helper_functions[] = {(void*)_ebpf_sock_ops_get_current_pid_tgid}; | ||
|
|
||
| static ebpf_helper_function_addresses_t _ebpf_sock_ops_global_helper_function_address_table = { | ||
| EBPF_HELPER_FUNCTION_ADDRESSES_HEADER, | ||
| EBPF_COUNT_OF(_ebpf_sock_ops_global_helper_functions), | ||
| (uint64_t*)_ebpf_sock_ops_global_helper_functions}; | ||
|
|
||
| static const void* _ebpf_sock_ops_program_type_specific_helper_functions[] = {(void*)_ebpf_sock_ops_get_flow_id}; | ||
|
|
||
| static ebpf_helper_function_addresses_t _ebpf_sock_ops_program_type_specific_helper_function_address_table = { | ||
| EBPF_HELPER_FUNCTION_ADDRESSES_HEADER, | ||
| EBPF_COUNT_OF(_ebpf_sock_ops_program_type_specific_helper_functions), | ||
| (uint64_t*)_ebpf_sock_ops_program_type_specific_helper_functions}; | ||
|
|
||
| static ebpf_result_t | ||
| _ebpf_sock_ops_context_create( | ||
| _In_reads_bytes_opt_(data_size_in) const uint8_t* data_in, | ||
|
|
@@ -839,8 +853,8 @@ _ebpf_sock_ops_context_create( | |
| *context = nullptr; | ||
|
|
||
| bpf_sock_ops_t* sock_ops_context = nullptr; | ||
| sock_ops_context_header_t* sock_ops_context_header = | ||
| reinterpret_cast<sock_ops_context_header_t*>(ebpf_allocate_with_tag(sizeof(sock_ops_context_header_t), EBPF_POOL_TAG_DEFAULT)); | ||
| sock_ops_context_header_t* sock_ops_context_header = reinterpret_cast<sock_ops_context_header_t*>( | ||
| ebpf_allocate_with_tag(sizeof(sock_ops_context_header_t), EBPF_POOL_TAG_DEFAULT)); | ||
| if (sock_ops_context_header == nullptr) { | ||
| retval = EBPF_NO_MEMORY; | ||
| goto Done; | ||
|
|
@@ -898,6 +912,8 @@ _ebpf_sock_ops_context_destroy( | |
| static ebpf_program_data_t _ebpf_sock_ops_program_data = { | ||
| .header = EBPF_PROGRAM_DATA_HEADER, | ||
| .program_info = &_ebpf_sock_ops_program_info, | ||
| .program_type_specific_helper_function_addresses = | ||
| &_ebpf_sock_ops_program_type_specific_helper_function_address_table, | ||
| .global_helper_function_addresses = &_ebpf_sock_ops_global_helper_function_address_table, | ||
| .context_create = &_ebpf_sock_ops_context_create, | ||
| .context_destroy = &_ebpf_sock_ops_context_destroy, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,140 @@ | ||
| // Copyright (c) eBPF for Windows contributors | ||
| // SPDX-License-Identifier: MIT | ||
|
|
||
| // Whenever this sample program changes, bpf2c_tests will fail unless the | ||
| // expected files in tests\bpf2c_tests\expected are updated. The following | ||
| // script can be used to regenerate the expected files: | ||
| // generate_expected_bpf2c_output.ps1 | ||
| // | ||
| // Usage: | ||
| // .\scripts\generate_expected_bpf2c_output.ps1 <build_output_path> | ||
| // Example: | ||
| // .\scripts\generate_expected_bpf2c_output.ps1 .\x64\Debug\ | ||
|
|
||
| #include "bpf_helpers.h" | ||
| #include "ebpf_nethooks.h" | ||
| #include "net/ip.h" | ||
| #include "socket_tests_common.h" | ||
|
|
||
| struct | ||
| { | ||
| __uint(type, BPF_MAP_TYPE_HASH); | ||
| __type(key, connection_tuple_t); | ||
| __type(value, uint64_t); | ||
| __uint(max_entries, 10); | ||
| } flow_id_map SEC(".maps"); | ||
|
|
||
| struct | ||
| { | ||
| __uint(type, BPF_MAP_TYPE_RINGBUF); | ||
| __uint(max_entries, 256 * 1024); | ||
| } flow_id_audit_map SEC(".maps"); | ||
|
|
||
| typedef struct _flow_id_audit_entry | ||
| { | ||
| connection_tuple_t tuple; | ||
| uint64_t flow_id; | ||
| uint64_t process_id; | ||
| uint32_t operation; | ||
| bool outbound; | ||
| bool connected; | ||
| } flow_id_audit_entry_t; | ||
|
|
||
| inline int | ||
| update_flow_id_audit_map(flow_id_audit_entry_t* audit_entry) | ||
| { | ||
| return bpf_ringbuf_output(&flow_id_audit_map, audit_entry, sizeof(*audit_entry), 0); | ||
| } | ||
|
|
||
| inline int | ||
| handle_v4_flow_id(bpf_sock_ops_t* ctx, bool outbound, bool connected) | ||
| { | ||
| int result = 0; | ||
| flow_id_audit_entry_t audit_entry = {0}; | ||
|
|
||
| // Get the WFP flow ID using the new helper function. | ||
| uint64_t flow_id = bpf_sock_ops_get_flow_id(ctx); | ||
|
|
||
| audit_entry.tuple.local_ip.ipv4 = ctx->local_ip4; | ||
| audit_entry.tuple.local_port = ctx->local_port; | ||
| audit_entry.tuple.remote_ip.ipv4 = ctx->remote_ip4; | ||
| audit_entry.tuple.remote_port = ctx->remote_port; | ||
| audit_entry.tuple.protocol = ctx->protocol; | ||
| audit_entry.tuple.interface_luid = ctx->interface_luid; | ||
| audit_entry.process_id = bpf_get_current_pid_tgid(); | ||
| // Ignore the thread Id. | ||
| audit_entry.process_id >>= 32; | ||
| audit_entry.outbound = outbound; | ||
| audit_entry.connected = connected; | ||
| audit_entry.operation = ctx->op; | ||
| audit_entry.flow_id = flow_id; | ||
|
|
||
| // Store the flow ID in our map for later verification. | ||
| bpf_map_update_elem(&flow_id_map, &audit_entry.tuple, &flow_id, BPF_ANY); | ||
|
|
||
| return update_flow_id_audit_map(&audit_entry); | ||
| } | ||
|
|
||
| inline int | ||
| handle_v6_flow_id(bpf_sock_ops_t* ctx, bool outbound, bool connected) | ||
| { | ||
| int result = 0; | ||
| flow_id_audit_entry_t audit_entry = {0}; | ||
|
|
||
| // Get the WFP flow ID using the new helper function. | ||
| uint64_t flow_id = bpf_sock_ops_get_flow_id(ctx); | ||
|
|
||
| // Copy IPv6 addresses. | ||
| __builtin_memcpy(&audit_entry.tuple.local_ip.ipv6, &ctx->local_ip6, sizeof(audit_entry.tuple.local_ip.ipv6)); | ||
| __builtin_memcpy(&audit_entry.tuple.remote_ip.ipv6, &ctx->remote_ip6, sizeof(audit_entry.tuple.remote_ip.ipv6)); | ||
|
|
||
| audit_entry.tuple.local_port = ctx->local_port; | ||
| audit_entry.tuple.remote_port = ctx->remote_port; | ||
| audit_entry.tuple.protocol = ctx->protocol; | ||
| audit_entry.tuple.interface_luid = ctx->interface_luid; | ||
| audit_entry.process_id = bpf_get_current_pid_tgid(); | ||
| // Ignore the thread Id. | ||
| audit_entry.process_id >>= 32; | ||
| audit_entry.outbound = outbound; | ||
| audit_entry.connected = connected; | ||
| audit_entry.operation = ctx->op; | ||
| audit_entry.flow_id = flow_id; | ||
|
|
||
| // Store the flow ID in our map for later verification | ||
| bpf_map_update_elem(&flow_id_map, &audit_entry.tuple, &flow_id, BPF_ANY); | ||
|
|
||
| return update_flow_id_audit_map(&audit_entry); | ||
| } | ||
|
|
||
| SEC("sockops") | ||
| int | ||
| flow_id_monitor(bpf_sock_ops_t* ctx) | ||
| { | ||
| switch (ctx->op) { | ||
| case BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB: | ||
| if (ctx->family == AF_INET) { | ||
| return handle_v4_flow_id(ctx, false, true); | ||
| } else if (ctx->family == AF_INET6) { | ||
| return handle_v6_flow_id(ctx, false, true); | ||
| } | ||
| break; | ||
| case BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB: | ||
| if (ctx->family == AF_INET) { | ||
| return handle_v4_flow_id(ctx, true, true); | ||
| } else if (ctx->family == AF_INET6) { | ||
| return handle_v6_flow_id(ctx, true, true); | ||
| } | ||
| break; | ||
| case BPF_SOCK_OPS_CONNECTION_DELETED_CB: | ||
| if (ctx->family == AF_INET) { | ||
| return handle_v4_flow_id(ctx, false, false); | ||
| } else if (ctx->family == AF_INET6) { | ||
| return handle_v6_flow_id(ctx, false, false); | ||
| } | ||
| break; | ||
| } | ||
|
|
||
| return 0; | ||
| } | ||
|
|
||
| char _license[] SEC("license") = "MIT"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we be particularly paranoid and follow WFP runtime checks?
local_flow_context->context.flow_id = 0;
if (FWPS_IS_METADATA_FIELD_PRESENT(incoming_metadata_values, FWPS_METADATA_FIELD_FLOW_HANDLE))
{
local_flow_context->context.flow_id = incoming_metadata_values->flowHandle;
}