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 program loading flags #3657

Closed
wants to merge 13 commits into from
15 changes: 15 additions & 0 deletions docs/eBpfExtensions.md
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,7 @@ This structure is used to specify the attach type supported by the extension for
contains the following fields:
* `supported_program_type`
* `bpf_attach_type`
* `capabilities`

The `supported_program_type` field of the struct should be filled with the `ebpf_program_type_t` (GUID) of the
supported program type. While attaching an eBPF program to a hook instance, the Execution Context enforces that the
Expand All @@ -373,6 +374,9 @@ requested attach type is supported by the Hook NPI provider. If not, the eBPF pr
The `bpf_attach_type` field should contain the equivalent bpf attach type integer. If there is no equivalent bpf
attach type, this field should be set to `0 (BPF_ATTACH_TYPE_UNSPEC)`.

The `capabilities` field is used to indicate what capabilities the attach provider supports. Currently only support_extension_data_v1 is defined, which determines if the execution context should offer a ebpf_extension_data_v0_t
or ebpf_extension_data_v1_t structure.

### 2.5 Hook NPI Client Attach and Detach Callbacks
The eBPF Execution Context registers a Hook NPI client module with the NMR for each program that is attached to a hook.
The attach type GUID is used as the NPI of the client module. And as a result, when an eBPF program gets attached to
Expand All @@ -393,6 +397,17 @@ ebpf_extension_data_t* extension_data = (ebpf_extension_data_t*)ClientRegistrati
attach_parameter = extension_data->data;
```

Note: This structure can be either version EBPF_ATTACH_CLIENT_DATA_VERSION_0 or version EBPF_ATTACH_CLIENT_DATA_VERSION_1.
It is strongly recommended that hook providers are able to support version 1 as version 0 will be eventually deprecated.


Changes from version 0 to version 1:
1. ebpf_extension_header_t was incorrectly used in version 0 to represent the size of the client data and not the
structure size.
2. A field for capabilities was added. Currently only prog_attach_flags is used to represent if the prog_attach_flags field is set.
2. Field data_size was added to contain the size of the client data.
3. An extension defined prog_attach_flags field was added.
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we add some details about what does prog_attach_flags mean.


The per-client data structure should be returned as the `ProviderBindingContext` output parameter.

Upon
Expand Down
2 changes: 2 additions & 0 deletions ebpfapi/Source.def
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ EXPORTS
bpf_program__attach_xdp
bpf_program__autoload
bpf_program__fd
bpf_program__flags
Alan-Jowett marked this conversation as resolved.
Show resolved Hide resolved
bpf_program__get_expected_attach_type
bpf_program__get_type=bpf_program__type
bpf_program__insn_cnt
Expand All @@ -90,6 +91,7 @@ EXPORTS
bpf_program__section_name
bpf_program__set_autoload
bpf_program__set_expected_attach_type
bpf_program__set_flags
bpf_program__set_type
bpf_program__type
bpf_program__unload
Expand Down
25 changes: 25 additions & 0 deletions include/bpf/libbpf.h
Original file line number Diff line number Diff line change
Expand Up @@ -917,6 +917,31 @@ void
ring_buffer__free(struct ring_buffer* rb);
/** @} */

/**
* @brief Query the BPF program flags.
Alan-Jowett marked this conversation as resolved.
Show resolved Hide resolved
*
* @param[in] prog A pointer to the BPF program.
* @return The flags of the BPF program.
*/
uint32_t
bpf_program__flags(const struct bpf_program* prog);

/**
* @brief Set the BPF program flags.
* The set of flags is defined by the program type. Neither libbpf nor the eBPF runtime check the flags and only
* the extension that handles this program type will interpret them. See the documentation for the
* program type for what flags are defined for that program type.
*
* @param[in] prog A pointer to the BPF program.
* @param[in] flags The flags to set.
*
* @retval 0 The operation was successful.
* @retval <0 An error occurred, and errno was set.
*
*/
int
bpf_program__set_flags(struct bpf_program* prog, uint32_t flags);

#else
#pragma warning(push)
#pragma warning(disable : 4200) // Zero-sized array in struct/union
Expand Down
43 changes: 40 additions & 3 deletions include/ebpf_extension.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
#include "ebpf_structs.h"
#include "ebpf_windows.h"

#pragma warning(push)
#pragma warning(disable : 4201) // nonstandard extension used: nameless struct/union

typedef ebpf_result_t (*_ebpf_extension_dispatch_function)();

typedef struct _ebpf_extension_dispatch_table
Expand Down Expand Up @@ -91,20 +94,52 @@ typedef struct _ebpf_extension_program_dispatch_table
ebpf_program_batch_end_invoke_function_t ebpf_program_batch_end_invoke_function;
} ebpf_extension_program_dispatch_table_t;

typedef struct _ebpf_extension_data
typedef struct _ebpf_extension_data_v0
{
ebpf_extension_header_t header;
const void* data;
} ebpf_extension_data_t;
} ebpf_extension_data_v0_t;

typedef struct _ebpf_extension_data_v1
{
ebpf_extension_header_t header;
const void* data;
union
{
uint64_t as_uint64;
struct
{
bool prog_attach_flags : 1; ///< Program attach flags are supported.
saxena-anurag marked this conversation as resolved.
Show resolved Hide resolved
};
} capabilities;
size_t data_size;
Alan-Jowett marked this conversation as resolved.
Show resolved Hide resolved
uint64_t prog_attach_flags;
} ebpf_extension_data_v1_t;

static_assert(
EBPF_FIELD_SIZE(ebpf_extension_data_v1_t, capabilities) == sizeof(uint64_t), "Size of capabilities is 64 bits.");

typedef ebpf_extension_data_v1_t ebpf_extension_data_t;

typedef struct _ebpf_attach_provider_data
{
ebpf_extension_header_t header;
ebpf_program_type_t supported_program_type;
bpf_attach_type_t bpf_attach_type;
enum bpf_link_type link_type;
union
{
uint64_t as_uint64;
struct
{
bool support_extension_data_v1 : 1; ///< Support extension data v1.
};
} capabilities;
} ebpf_attach_provider_data_t;

static_assert(
EBPF_FIELD_SIZE(ebpf_attach_provider_data_t, capabilities) == sizeof(uint64_t), "Size of capabilities is 64 bits.");

/***
* The state of the execution context when the eBPF program was invoked.
* This is used to cache state that won't change during the execution of
Expand All @@ -126,4 +161,6 @@ typedef struct _ebpf_execution_context_state
} tail_call_state;
} ebpf_execution_context_state_t;

#define EBPF_CONTEXT_HEADER uint64_t context_header[8]
#define EBPF_CONTEXT_HEADER uint64_t context_header[8]

#pragma warning(pop)
26 changes: 24 additions & 2 deletions include/ebpf_windows.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,34 @@ typedef enum _ebpf_helper_function

#define EBPF_MAX_GENERAL_HELPER_FUNCTION 0xFFFF

// Warning: Version 1 of the eBPF extension data structures will be deprecated in a future release.
// All extension data structures should be updated to version 2.
// Leaving version 1 for backward compatibility for now.
Alan-Jowett marked this conversation as resolved.
Show resolved Hide resolved
#define EBPF_ATTACH_CLIENT_DATA_VERSION_0 0
#define EBPF_ATTACH_CLIENT_DATA_VERSION_1 1
#define EBPF_ATTACH_CLIENT_DATA_CURRENT_VERSION 1
#define EBPF_PROGRAM_INFORMATION_CLIENT_DATA_CURRENT_VERSION 1

#define EBPF_ATTACH_CLIENT_DATA_VERSION_0_SIZE EBPF_SIZE_INCLUDING_FIELD(ebpf_extension_data_v0_t, data)
#define EBPF_ATTACH_CLIENT_DATA_VERSION_0_TOTAL_SIZE sizeof(ebpf_extension_data_v0_t)
#define EBPF_ATTACH_CLIENT_DATA_HEADER_VERSION_0 \
{ \
EBPF_ATTACH_CLIENT_DATA_VERSION_0, EBPF_ATTACH_CLIENT_DATA_VERSION_0_SIZE, \
EBPF_ATTACH_CLIENT_DATA_VERSION_0_TOTAL_SIZE \
}

#define EBPF_ATTACH_CLIENT_DATA_VERSION_1_SIZE EBPF_SIZE_INCLUDING_FIELD(ebpf_extension_data_v1_t, prog_attach_flags)
#define EBPF_ATTACH_CLIENT_DATA_VERSION_1_TOTAL_SIZE sizeof(ebpf_extension_data_v1_t)
#define EBPF_ATTACH_CLIENT_DATA_HEADER_VERSION_1 \
{ \
EBPF_ATTACH_CLIENT_DATA_VERSION_1, EBPF_ATTACH_CLIENT_DATA_VERSION_1_SIZE, \
EBPF_ATTACH_CLIENT_DATA_VERSION_1_TOTAL_SIZE \
}

#define EBPF_PROGRAM_INFORMATION_CLIENT_DATA_CURRENT_VERSION 1
// Version 1 of the eBPF extension data structures and their lengths.
#define EBPF_ATTACH_PROVIDER_DATA_CURRENT_VERSION 1
#define EBPF_ATTACH_PROVIDER_DATA_CURRENT_VERSION_SIZE EBPF_SIZE_INCLUDING_FIELD(ebpf_attach_provider_data_t, link_type)
#define EBPF_ATTACH_PROVIDER_DATA_CURRENT_VERSION_SIZE \
EBPF_SIZE_INCLUDING_FIELD(ebpf_attach_provider_data_t, capabilities)
#define EBPF_ATTACH_PROVIDER_DATA_CURRENT_VERSION_TOTAL_SIZE sizeof(ebpf_attach_provider_data_t)
#define EBPF_ATTACH_PROVIDER_DATA_HEADER \
{ \
Expand Down
13 changes: 13 additions & 0 deletions libs/api/api_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ typedef struct bpf_program
bool pinned;
const char* log_buffer;
uint32_t log_buffer_size;
uint64_t flags;
} ebpf_program_t;

typedef struct bpf_map
Expand Down Expand Up @@ -759,3 +760,15 @@ ebpf_api_thread_local_cleanup() noexcept;
*/
void
ebpf_api_thread_local_initialize() noexcept;

/**
* @brief Set the flags on a program
*
* @param[in] program_fd File descriptor for the program.
* @param[in] flags Flags to set on the program.
*
* @retval EBPF_SUCCESS The operation was successful.
* @retval EBPF_INVALID_ARGUMENT One or more parameters are wrong.
*/
_Must_inspect_result_ ebpf_result_t
ebpf_program_set_flags(fd_t program_fd, uint64_t flags) noexcept;
30 changes: 30 additions & 0 deletions libs/api/ebpf_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2129,6 +2129,12 @@ _initialize_ebpf_programs_native(
result = EBPF_NO_MEMORY;
goto Exit;
}
if (program->flags != 0) {
Alan-Jowett marked this conversation as resolved.
Show resolved Hide resolved
result = ebpf_program_set_flags(program->fd, program->flags);
if (result != EBPF_SUCCESS) {
goto Exit;
}
}
program->handle = program_handles[i];
program_handles[i] = ebpf_handle_invalid;
program->program_type = info.type_uuid;
Expand Down Expand Up @@ -3291,6 +3297,13 @@ _Requires_lock_not_held_(_ebpf_state_mutex) static ebpf_result_t

program->fd = _create_file_descriptor_for_handle(program->handle);

if (program->flags != 0) {
result = ebpf_program_set_flags(program->fd, program->flags);
if (result != EBPF_SUCCESS) {
break;
}
}

// Populate load_info.
ebpf_program_load_info load_info = {0};
load_info.object_name = const_cast<char*>(object->object_name);
Expand Down Expand Up @@ -4670,3 +4683,20 @@ ebpf_api_thread_local_initialize() noexcept
// Nothing to do.
// Added for symmetry with ebpf_api_thread_local_cleanup.
}

_Must_inspect_result_ ebpf_result_t
ebpf_program_set_flags(fd_t program_fd, uint64_t flags) noexcept
{
ebpf_handle_t program_handle = _get_handle_from_file_descriptor(program_fd);
if (program_handle == ebpf_handle_invalid) {
return EBPF_INVALID_FD;
}

ebpf_operation_program_set_flags_request_t request;
request.header.id = ebpf_operation_id_t::EBPF_OPERATION_PROGRAM_SET_FLAGS;
request.header.length = sizeof(request);
request.program_handle = program_handle;
request.flags = flags;

return win32_error_code_to_ebpf_result(invoke_ioctl(request));
}
16 changes: 16 additions & 0 deletions libs/api/libbpf_program.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -755,3 +755,19 @@ bpf_prog_test_run_opts(int prog_fd, struct bpf_test_run_opts* opts)

return 0;
}

__u32
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should this be __u64? and also below in set_flags?

bpf_program__flags(const struct bpf_program* prog)
{
return static_cast<uint32_t>(prog->flags);
}

int
bpf_program__set_flags(struct bpf_program* prog, __u32 flags)
{
if (prog->object->loaded) {
return libbpf_err(-EBUSY);
}
prog->flags = flags;
return 0;
}
22 changes: 22 additions & 0 deletions libs/execution_context/ebpf_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -2083,6 +2083,27 @@ _ebpf_core_protocol_ring_buffer_map_async_query(
return result;
}

static ebpf_result_t
_ebpf_core_protocol_program_set_flags(_In_ const ebpf_operation_program_set_flags_request_t* request)
{
EBPF_LOG_ENTRY();

ebpf_program_t* program = NULL;

ebpf_result_t result =
EBPF_OBJECT_REFERENCE_BY_HANDLE(request->program_handle, EBPF_OBJECT_PROGRAM, (ebpf_core_object_t**)&program);

if (result != EBPF_SUCCESS) {
goto Exit;
}

ebpf_program_set_flags(program, request->flags);

Exit:
EBPF_OBJECT_RELEASE_REFERENCE((ebpf_core_object_t*)program);
EBPF_RETURN_RESULT(result);
}

static ebpf_result_t
_ebpf_core_protocol_ring_buffer_map_write_data(_In_ const ebpf_operation_ring_buffer_map_write_data_request_t* request)
{
Expand Down Expand Up @@ -2656,6 +2677,7 @@ static ebpf_protocol_handler_t _ebpf_protocol_handlers[] = {
DECLARE_PROTOCOL_HANDLER_VARIABLE_REQUEST_FIXED_REPLY(map_delete_element_batch, keys, PROTOCOL_ALL_MODES),
DECLARE_PROTOCOL_HANDLER_VARIABLE_REQUEST_VARIABLE_REPLY(
map_get_next_key_value_batch, previous_key, data, PROTOCOL_ALL_MODES),
DECLARE_PROTOCOL_HANDLER_FIXED_REQUEST_NO_REPLY(program_set_flags, PROTOCOL_ALL_MODES),
};

_Must_inspect_result_ ebpf_result_t
Expand Down
Loading
Loading