diff --git a/include/ebpf_core_structs.h b/include/ebpf_core_structs.h index 1517aaef05..c59acb285c 100644 --- a/include/ebpf_core_structs.h +++ b/include/ebpf_core_structs.h @@ -31,3 +31,11 @@ typedef struct _ebpf_ring_buffer_map_async_query_result size_t producer; size_t consumer; } ebpf_ring_buffer_map_async_query_result_t; + +typedef enum _ebpf_object_type +{ + EBPF_OBJECT_UNKNOWN, + EBPF_OBJECT_MAP, + EBPF_OBJECT_LINK, + EBPF_OBJECT_PROGRAM, +} ebpf_object_type_t; diff --git a/include/linux/bpf.h b/include/linux/bpf.h index eefd2d3756..ec346f9e98 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -54,6 +54,8 @@ enum bpf_stats_type // Names do not have to match, but try to keep them the same as much as possible. // In case of conflicts prefix them with "sys_" or "SYS_". +#define SYS_BPF_OBJ_NAME_LEN 16U + enum bpf_cmd_id { BPF_MAP_CREATE, @@ -88,12 +90,15 @@ enum bpf_cmd_id /// Attributes used by BPF_MAP_CREATE. typedef struct { - enum bpf_map_type map_type; ///< Type of map to create. - uint32_t key_size; ///< Size in bytes of keys. - uint32_t value_size; ///< Size in bytes of values. - uint32_t max_entries; ///< Maximum number of entries in the map. - uint32_t map_flags; ///< Not supported, must be zero. - uint32_t inner_map_fd; ///< File descriptor of inner map. + enum bpf_map_type map_type; ///< Type of map to create. + uint32_t key_size; ///< Size in bytes of keys. + uint32_t value_size; ///< Size in bytes of values. + uint32_t max_entries; ///< Maximum number of entries in the map. + uint32_t map_flags; ///< Not supported, must be zero. + uint32_t inner_map_fd; ///< File descriptor of inner map. + uint32_t numa_node; ///< Not supported, must be zero. + char map_name[SYS_BPF_OBJ_NAME_LEN]; ///< Map name. + uint32_t map_ifindex; ///< Not supported, must be zero. } sys_bpf_map_create_attr_t; typedef struct @@ -137,6 +142,7 @@ typedef struct uint64_t log_buf; ///< Pointer to a buffer in which log info can be written. uint32_t kern_version; ///< Kernel version (currently ignored on Windows). uint32_t prog_flags; ///< Not supported, must be zero. + char prog_name[SYS_BPF_OBJ_NAME_LEN]; ///< Program name. } sys_bpf_prog_load_attr_t; typedef struct @@ -186,6 +192,40 @@ typedef struct uint64_t info; ///< Pointer to memory in which to write the info obtained. } sys_bpf_obj_info_attr_t; +typedef struct +{ + ebpf_map_type_t type; ///< Type of map. + ebpf_id_t id; ///< Map ID. + uint32_t key_size; ///< Size in bytes of a map key. + uint32_t value_size; ///< Size in bytes of a map value. + uint32_t max_entries; ///< Maximum number of entries allowed in the map. + uint32_t map_flags; ///< Map flags. + char name[SYS_BPF_OBJ_NAME_LEN]; ///< Null-terminated map name. +} sys_bpf_map_info_t; + +typedef struct +{ + enum bpf_prog_type type; ///< Program type. + ebpf_id_t id; ///< Program ID. + char tag[8]; ///< Program tag. + uint32_t jited_prog_len; ///< Not supported. + uint32_t xlated_prog_len; ///< Not supported. + uint64_t jited_prog_insns; ///< Not supported. + uint64_t xlated_prog_insns; ///< Not supported. + uint64_t load_time; ///< Not supported. + uint32_t created_by_uid; ///< Not supported. + uint32_t nr_map_ids; ///< Number of maps associated with this program. + uint64_t map_ids; ///< Pointer to caller-allocated array to fill map IDs into. + char name[SYS_BPF_OBJ_NAME_LEN]; ///< Null-terminated program name. +} sys_bpf_prog_info_t; + +typedef struct +{ + enum bpf_link_type type; ///< Link type. + ebpf_id_t id; ///< Link ID. + ebpf_id_t prog_id; ///< Program ID. +} sys_bpf_link_info_t; + /// Attributes used by BPF_LINK_DETACH. typedef struct { diff --git a/libs/api/api_internal.h b/libs/api/api_internal.h index 5cb188f143..0096acf943 100644 --- a/libs/api/api_internal.h +++ b/libs/api/api_internal.h @@ -521,7 +521,10 @@ ebpf_get_next_program_id(ebpf_id_t start_id, ebpf_id_t _Out_* next_id) noexcept; */ _Must_inspect_result_ ebpf_result_t ebpf_object_get_info_by_fd( - fd_t bpf_fd, _Inout_updates_bytes_to_(*info_size, *info_size) void* info, _Inout_ uint32_t* info_size) noexcept; + fd_t bpf_fd, + _Inout_updates_bytes_to_(*info_size, *info_size) void* info, + _Inout_ uint32_t* info_size, + _Out_opt_ ebpf_object_type_t* type) noexcept; /** * @brief Pin an object to the specified path. diff --git a/libs/api/bpf_syscall.cpp b/libs/api/bpf_syscall.cpp index a49238e7b7..08a776d59f 100644 --- a/libs/api/bpf_syscall.cpp +++ b/libs/api/bpf_syscall.cpp @@ -1,7 +1,10 @@ // Copyright (c) eBPF for Windows contributors // SPDX-License-Identifier: MIT +#include "api_internal.h" #include "bpf.h" #include "libbpf.h" +#include "libbpf_internal.h" +#include "linux/bpf.h" #include #include @@ -69,6 +72,105 @@ template class ExtensibleStruct } }; +static bool +is_valid_name(_In_z_ const char* name, _Out_opt_ size_t* length) +{ + size_t name_length = strnlen(name, SYS_BPF_OBJ_NAME_LEN); + + if (length != NULL) { + *length = name_length; + } + + return name_length < SYS_BPF_OBJ_NAME_LEN; +} + +static void +convert_to_map_info(_Out_ struct bpf_map_info* bpf, _In_ const sys_bpf_map_info_t* sys); +static void +convert_to_sys_map_info(_Out_ sys_bpf_map_info_t* sys, _In_ const struct bpf_map_info* bpf); +static void +convert_to_prog_info(_Out_ struct bpf_prog_info* bpf, _In_ const sys_bpf_prog_info_t* sys); +static void +convert_to_sys_prog_info(_Out_ sys_bpf_prog_info_t* sys, _In_ const struct bpf_prog_info* bpf); +static void +convert_to_link_info(_Out_ struct bpf_link_info* bpf, _In_ const sys_bpf_link_info_t* sys); +static void +convert_to_sys_link_info(_Out_ sys_bpf_link_info_t* sys, _In_ const struct bpf_link_info* bpf); + +static int +obj_get_info_by_fd(_In_ sys_bpf_obj_info_attr_t* attr) +{ + union + { + struct bpf_map_info map; + struct bpf_prog_info prog; + struct bpf_link_info link; + } tmp = {}; + uint32_t info_size = sizeof(tmp); + ebpf_object_type_t type; + + ebpf_result_t result = ebpf_object_get_info_by_fd((fd_t)attr->bpf_fd, &tmp, &info_size, &type); + if (result != EBPF_SUCCESS) { + return libbpf_result_err(result); + } + + switch (type) { + case EBPF_OBJECT_MAP: { + ExtensibleStruct info((void*)attr->info, (size_t)attr->info_len); + + convert_to_map_info(&tmp.map, &info); + + info_size = sizeof(tmp.map); + result = ebpf_object_get_info_by_fd((fd_t)attr->bpf_fd, &tmp.map, &info_size, nullptr); + if (result != EBPF_SUCCESS) { + return libbpf_result_err(result); + } + + convert_to_sys_map_info(&info, &tmp.map); + return 0; + } + + case EBPF_OBJECT_PROGRAM: { + ExtensibleStruct info((void*)attr->info, (size_t)attr->info_len); + sys_bpf_prog_info_t* sys = &info; + + if (sys->jited_prog_len != 0 || sys->xlated_prog_len != 0 || sys->jited_prog_insns != 0 || + sys->xlated_prog_insns != 0) { + return -EINVAL; + } + + convert_to_prog_info(&tmp.prog, &info); + + info_size = sizeof(tmp.prog); + result = ebpf_object_get_info_by_fd((fd_t)attr->bpf_fd, &tmp.prog, &info_size, nullptr); + if (result != EBPF_SUCCESS) { + return libbpf_result_err(result); + } + + convert_to_sys_prog_info(&info, &tmp.prog); + return 0; + } + + case EBPF_OBJECT_LINK: { + ExtensibleStruct info((void*)attr->info, (size_t)attr->info_len); + + convert_to_link_info(&tmp.link, &info); + + info_size = sizeof(tmp.link); + result = ebpf_object_get_info_by_fd((fd_t)attr->bpf_fd, &tmp.link, &info_size, nullptr); + if (result != EBPF_SUCCESS) { + return libbpf_result_err(result); + } + + convert_to_sys_link_info(&info, &tmp.link); + return 0; + } + + default: + return -EINVAL; + } +} + int bpf(int cmd, union bpf_attr* attr, unsigned int size) { @@ -97,11 +199,17 @@ bpf(int cmd, union bpf_attr* attr, unsigned int size) struct bpf_map_create_opts opts = { .inner_map_fd = map_create_attr->inner_map_fd, .map_flags = map_create_attr->map_flags, + .numa_node = map_create_attr->numa_node, + .map_ifindex = map_create_attr->map_ifindex, }; + if (!is_valid_name(map_create_attr->map_name, NULL)) { + return -EINVAL; + } + return bpf_map_create( map_create_attr->map_type, - nullptr, + map_create_attr->map_name, map_create_attr->key_size, map_create_attr->value_size, map_create_attr->max_entries, @@ -175,8 +283,7 @@ bpf(int cmd, union bpf_attr* attr, unsigned int size) } case BPF_OBJ_GET_INFO_BY_FD: { ExtensibleStruct info_by_fd_attr((void*)attr, (size_t)size); - return bpf_obj_get_info_by_fd( - info_by_fd_attr->bpf_fd, (void*)info_by_fd_attr->info, &info_by_fd_attr->info_len); + return obj_get_info_by_fd(&info_by_fd_attr); } case BPF_OBJ_PIN: { ExtensibleStruct obj_pin_attr((void*)attr, (size_t)size); @@ -212,10 +319,21 @@ bpf(int cmd, union bpf_attr* attr, unsigned int size) .log_size = prog_load_attr->log_size, .log_buf = (char*)prog_load_attr->log_buf, }; + const char* name = prog_load_attr->prog_name; + size_t name_length; + + if (!is_valid_name(name, &name_length)) { + return -EINVAL; + } + + if (name_length == 0) { + // Disable using sha256 as object name. + name = ""; + } return bpf_prog_load( prog_load_attr->prog_type, - nullptr, + name, (const char*)prog_load_attr->license, (const struct bpf_insn*)prog_load_attr->insns, prog_load_attr->insn_cnt, @@ -262,3 +380,71 @@ bpf(int cmd, union bpf_attr* attr, unsigned int size) return -EINVAL; } } + +#define BPF_TO_SYS(field) sys->field = bpf->field +#define BPF_TO_SYS_STR(field) strncpy_s(sys->field, sizeof(sys->field), bpf->field, _TRUNCATE) +#define BPF_TO_SYS_MEM(field) memcpy(sys->field, bpf->field, sizeof(sys->field)) +#define SYS_TO_BPF(field) bpf->field = sys->field +#define SYS_TO_BPF_STR(field) strncpy_s(bpf->field, sys->field, sizeof(bpf->field)) +#define SYS_TO_BPF_MEM(field) memcpy(bpf->field, sys->field, sizeof(bpf->field)) + +static void +convert_to_map_info(_Out_ struct bpf_map_info* bpf, _In_ const sys_bpf_map_info_t* sys) +{ + SYS_TO_BPF(type); + SYS_TO_BPF(id); + SYS_TO_BPF(key_size); + SYS_TO_BPF(value_size); + SYS_TO_BPF(max_entries); + SYS_TO_BPF(map_flags); + SYS_TO_BPF_STR(name); +} + +static void +convert_to_sys_map_info(_Out_ sys_bpf_map_info_t* sys, _In_ const struct bpf_map_info* bpf) +{ + BPF_TO_SYS(type); + BPF_TO_SYS(id); + BPF_TO_SYS(key_size); + BPF_TO_SYS(value_size); + BPF_TO_SYS(max_entries); + BPF_TO_SYS(map_flags); + BPF_TO_SYS_STR(name); +} + +static void +convert_to_prog_info(_Out_ struct bpf_prog_info* bpf, _In_ const sys_bpf_prog_info_t* sys) +{ + SYS_TO_BPF(type); + SYS_TO_BPF(id); + SYS_TO_BPF(nr_map_ids); + SYS_TO_BPF(map_ids); + SYS_TO_BPF_STR(name); +} + +static void +convert_to_sys_prog_info(_Out_ sys_bpf_prog_info_t* sys, _In_ const struct bpf_prog_info* bpf) +{ + *sys = {}; + BPF_TO_SYS(type); + BPF_TO_SYS(id); + BPF_TO_SYS(nr_map_ids); + BPF_TO_SYS(map_ids); + BPF_TO_SYS_STR(name); +} + +static void +convert_to_link_info(_Out_ struct bpf_link_info* bpf, _In_ const sys_bpf_link_info_t* sys) +{ + SYS_TO_BPF(type); + SYS_TO_BPF(id); + SYS_TO_BPF(prog_id); +} + +static void +convert_to_sys_link_info(_Out_ sys_bpf_link_info_t* sys, _In_ const struct bpf_link_info* bpf) +{ + BPF_TO_SYS(type); + BPF_TO_SYS(id); + BPF_TO_SYS(prog_id); +} diff --git a/libs/api/ebpf_api.cpp b/libs/api/ebpf_api.cpp index 366e7b553b..1113c01338 100644 --- a/libs/api/ebpf_api.cpp +++ b/libs/api/ebpf_api.cpp @@ -316,7 +316,7 @@ ebpf_map_create( ebpf_assert(map_fd); - if (opts && opts->map_flags != 0) { + if (opts && (opts->map_flags != 0 || opts->numa_node != 0 || opts->map_ifindex != 0)) { result = EBPF_INVALID_ARGUMENT; goto Exit; } @@ -2016,7 +2016,7 @@ initialize_map(_Out_ ebpf_map_t* map, _In_ const map_cache_t& map_cache) noexcep if (map_cache.verifier_map_descriptor.inner_map_fd != ebpf_fd_invalid) { struct bpf_map_info info = {0}; uint32_t info_size = (uint32_t)sizeof(info); - if (ebpf_object_get_info_by_fd(map_cache.verifier_map_descriptor.inner_map_fd, &info, &info_size) == + if (ebpf_object_get_info_by_fd(map_cache.verifier_map_descriptor.inner_map_fd, &info, &info_size, NULL) == EBPF_SUCCESS) { map->map_definition.inner_map_id = info.id; } @@ -2049,7 +2049,7 @@ _initialize_ebpf_maps_native( } struct bpf_map_info info = {0}; uint32_t info_size = (uint32_t)sizeof(info); - result = ebpf_object_get_info(map_handles[i], &info, &info_size); + result = ebpf_object_get_info(map_handles[i], &info, &info_size, NULL); if (result != EBPF_SUCCESS) { goto Exit; } @@ -2107,7 +2107,7 @@ _initialize_ebpf_programs_native( } struct bpf_prog_info info = {}; uint32_t info_size = (uint32_t)sizeof(info); - result = ebpf_object_get_info(program_handles[i], &info, &info_size); + result = ebpf_object_get_info(program_handles[i], &info, &info_size, NULL); if (result != EBPF_SUCCESS) { goto Exit; } @@ -2983,7 +2983,7 @@ _ebpf_validate_map(_In_ const ebpf_map_t* map, fd_t original_map_fd) NO_EXCEPT_T struct bpf_map_info info = {0}; fd_t inner_map_info_fd = ebpf_fd_invalid; uint32_t info_size = (uint32_t)sizeof(info); - ebpf_result_t result = ebpf_object_get_info_by_fd(original_map_fd, &info, &info_size); + ebpf_result_t result = ebpf_object_get_info_by_fd(original_map_fd, &info, &info_size, NULL); if (result != EBPF_SUCCESS) { result = EBPF_INVALID_ARGUMENT; goto Exit; @@ -4067,7 +4067,10 @@ CATCH_NO_MEMORY_EBPF_RESULT _Must_inspect_result_ ebpf_result_t ebpf_object_get_info_by_fd( - fd_t bpf_fd, _Inout_updates_bytes_to_(*info_size, *info_size) void* info, _Inout_ uint32_t* info_size) NO_EXCEPT_TRY + fd_t bpf_fd, + _Inout_updates_bytes_to_(*info_size, *info_size) void* info, + _Inout_ uint32_t* info_size, + _Out_opt_ ebpf_object_type_t* type) NO_EXCEPT_TRY { EBPF_LOG_ENTRY(); ebpf_assert(info); @@ -4078,7 +4081,7 @@ ebpf_object_get_info_by_fd( EBPF_RETURN_RESULT(EBPF_INVALID_FD); } - EBPF_RETURN_RESULT(ebpf_object_get_info(handle, info, info_size)); + EBPF_RETURN_RESULT(ebpf_object_get_info(handle, info, info_size, type)); } CATCH_NO_MEMORY_EBPF_RESULT diff --git a/libs/api/libbpf_object.cpp b/libs/api/libbpf_object.cpp index 2256a8c385..d58742f81b 100644 --- a/libs/api/libbpf_object.cpp +++ b/libs/api/libbpf_object.cpp @@ -70,7 +70,7 @@ bpf_object__next(struct bpf_object* prev) int bpf_obj_get_info_by_fd(int bpf_fd, void* info, __u32* info_len) { - return libbpf_result_err(ebpf_object_get_info_by_fd((fd_t)bpf_fd, info, info_len)); + return libbpf_result_err(ebpf_object_get_info_by_fd((fd_t)bpf_fd, info, info_len, NULL)); } int diff --git a/libs/api_common/api_common.cpp b/libs/api_common/api_common.cpp index a8ea22b1c5..2d9d86ed3e 100644 --- a/libs/api_common/api_common.cpp +++ b/libs/api_common/api_common.cpp @@ -61,7 +61,8 @@ _Must_inspect_result_ ebpf_result_t ebpf_object_get_info( ebpf_handle_t handle, _Inout_updates_bytes_to_(*info_size, *info_size) void* info, - _Inout_ uint32_t* info_size) noexcept + _Inout_ uint32_t* info_size, + _Out_opt_ ebpf_object_type_t* type) noexcept { EBPF_LOG_ENTRY(); if (info == nullptr || info_size == nullptr) { @@ -84,6 +85,9 @@ ebpf_object_get_info( ebpf_result_t result = win32_error_code_to_ebpf_result(invoke_ioctl(request_buffer, reply_buffer)); if (result == EBPF_SUCCESS) { + if (type != NULL) { + *type = reply->type; + } *info_size = reply->header.length - EBPF_OFFSET_OF(ebpf_operation_get_object_info_reply_t, info); memcpy(info, reply->info, *info_size); } @@ -103,7 +107,7 @@ query_map_definition( { bpf_map_info info = {0}; uint32_t info_size = sizeof(info); - ebpf_result_t result = ebpf_object_get_info(handle, &info, &info_size); + ebpf_result_t result = ebpf_object_get_info(handle, &info, &info_size, NULL); if (result == EBPF_SUCCESS) { *id = info.id; *type = info.type; diff --git a/libs/api_common/api_common.hpp b/libs/api_common/api_common.hpp index 9167ebc877..58838ab0f2 100644 --- a/libs/api_common/api_common.hpp +++ b/libs/api_common/api_common.hpp @@ -192,7 +192,8 @@ _Must_inspect_result_ ebpf_result_t ebpf_object_get_info( ebpf_handle_t handle, _Inout_updates_bytes_to_(*info_size, *info_size) void* info, - _Inout_ uint32_t* info_size) noexcept; + _Inout_ uint32_t* info_size, + _Out_opt_ ebpf_object_type_t* type) noexcept; _Must_inspect_result_ ebpf_result_t query_map_definition( diff --git a/libs/execution_context/ebpf_core.c b/libs/execution_context/ebpf_core.c index d08b51c281..027f032eac 100644 --- a/libs/execution_context/ebpf_core.c +++ b/libs/execution_context/ebpf_core.c @@ -2077,6 +2077,7 @@ _ebpf_core_protocol_get_object_info( } if (result == EBPF_SUCCESS) { + reply->type = object->type; reply->header.length = FIELD_OFFSET(ebpf_operation_get_object_info_reply_t, info) + info_size; } EBPF_OBJECT_RELEASE_REFERENCE(object); diff --git a/libs/execution_context/ebpf_link.c b/libs/execution_context/ebpf_link.c index 8c471c4a11..ac4a210c0b 100644 --- a/libs/execution_context/ebpf_link.c +++ b/libs/execution_context/ebpf_link.c @@ -652,7 +652,7 @@ ebpf_link_get_info( memset(info, 0, sizeof(*info)); info->id = link->object.id; - info->prog_id = (link->program) ? ((ebpf_core_object_t*)link->program)->id : EBPF_ID_NONE; + info->prog_id = (link->program) ? ((ebpf_core_object_t*)link->program)->id : 0; info->type = link->link_type; info->program_type_uuid = link->program_type; info->attach_type_uuid = link->attach_type; diff --git a/libs/execution_context/ebpf_protocol.h b/libs/execution_context/ebpf_protocol.h index b17ba0810b..7f5d678909 100644 --- a/libs/execution_context/ebpf_protocol.h +++ b/libs/execution_context/ebpf_protocol.h @@ -359,6 +359,7 @@ typedef struct _ebpf_operation_get_object_info_request typedef struct _ebpf_operation_get_object_info_reply { struct _ebpf_operation_header header; + ebpf_object_type_t type; uint8_t info[1]; } ebpf_operation_get_object_info_reply_t; diff --git a/libs/runtime/ebpf_object.h b/libs/runtime/ebpf_object.h index 1bd55a267c..33ca03142c 100644 --- a/libs/runtime/ebpf_object.h +++ b/libs/runtime/ebpf_object.h @@ -111,14 +111,6 @@ extern "C" EBPF_FILE_ID, \ __LINE__) - typedef enum _ebpf_object_type - { - EBPF_OBJECT_UNKNOWN, - EBPF_OBJECT_MAP, - EBPF_OBJECT_LINK, - EBPF_OBJECT_PROGRAM, - } ebpf_object_type_t; - typedef struct _ebpf_base_object ebpf_base_object_t; typedef void (*ebpf_base_release_reference_t)(_Inout_ void* base_object, ebpf_file_id_t file_id, uint32_t line); typedef void (*ebpf_base_acquire_reference_t)(_Inout_ void* base_object, ebpf_file_id_t file_id, uint32_t line); diff --git a/tests/unit/libbpf_test.cpp b/tests/unit/libbpf_test.cpp index 7460a566ec..f5a5efe93b 100644 --- a/tests/unit/libbpf_test.cpp +++ b/tests/unit/libbpf_test.cpp @@ -2165,7 +2165,7 @@ TEST_CASE("enumerate link IDs", "[libbpf]") REQUIRE(errno == ENOENT); } -TEST_CASE("enumerate link IDs with bpf", "[libbpf]") +TEST_CASE("enumerate link IDs with bpf", "[libbpf][bpf]") { _test_helper_end_to_end test_helper; test_helper.initialize(); @@ -2217,12 +2217,14 @@ TEST_CASE("enumerate link IDs with bpf", "[libbpf]") // Get info on the first link. memset(&attr, 0, sizeof(attr)); - bpf_link_info info; + sys_bpf_link_info_t info = {}; attr.info.bpf_fd = fd1; attr.info.info = (uintptr_t)&info; attr.info.info_len = sizeof(info); REQUIRE(bpf(BPF_OBJ_GET_INFO_BY_FD, &attr, sizeof(attr)) == 0); - REQUIRE(info.attach_type_uuid == EBPF_ATTACH_TYPE_SAMPLE); + REQUIRE(info.type == BPF_LINK_TYPE_UNSPEC); + REQUIRE(info.id == id1); + REQUIRE(info.prog_id != 0); // Detach the first link. memset(&attr, 0, sizeof(attr)); @@ -2235,7 +2237,9 @@ TEST_CASE("enumerate link IDs with bpf", "[libbpf]") attr.info.info = (uintptr_t)&info; attr.info.info_len = sizeof(info); REQUIRE(bpf(BPF_OBJ_GET_INFO_BY_FD, &attr, sizeof(attr)) == 0); - REQUIRE(info.attach_type_uuid == EBPF_ATTACH_TYPE_SAMPLE); + REQUIRE(info.type == BPF_LINK_TYPE_UNSPEC); + REQUIRE(info.id == id1); + REQUIRE(info.prog_id == 0); // Pin the detached link. memset(&attr, 0, sizeof(attr)); @@ -2259,6 +2263,19 @@ TEST_CASE("enumerate link IDs with bpf", "[libbpf]") REQUIRE(bpf(BPF_OBJ_GET_INFO_BY_FD, &attr, sizeof(attr)) == 0); REQUIRE(info.id == id1); + // Get info on the second link. + memset(&attr, 0, sizeof(attr)); + info = {}; + attr.info.bpf_fd = fd2; + attr.info.info = (uintptr_t)&info; + attr.info.info_len = sizeof(info); + REQUIRE(bpf(BPF_OBJ_GET_INFO_BY_FD, &attr, sizeof(attr)) == 0); + // TODO: Should this be BPF_LINK_TYPE_PLAIN? + // See https://github.com/microsoft/ebpf-for-windows/issues/4096 + REQUIRE(info.type == BPF_LINK_TYPE_UNSPEC); + REQUIRE(info.id == id2); + REQUIRE(info.prog_id != 0); + // And for completeness, try an invalid bpf() call. REQUIRE(bpf(-1, &attr, sizeof(attr)) == -EINVAL); @@ -2887,7 +2904,7 @@ TEST_CASE("bpf_object__load with .o from memory", "[libbpf]") } // Test that bpf() accepts a smaller and a larger bpf_attr. -TEST_CASE("bpf() backwards compatibility", "[libbpf]") +TEST_CASE("bpf() backwards compatibility", "[libbpf][bpf]") { _test_helper_libbpf test_helper; test_helper.initialize(); @@ -2925,7 +2942,7 @@ TEST_CASE("bpf() backwards compatibility", "[libbpf]") // BPF_PROG_LOAD, BPF_OBJ_GET_INFO_BY_FD, BPF_PROG_GET_NEXT_ID, // BPF_MAP_CREATE, BPF_MAP_GET_NEXT_ID, BPF_PROG_BIND_MAP, // BPF_PROG_GET_FD_BY_ID, BPF_MAP_GET_FD_BY_ID, and BPF_MAP_GET_FD_BY_ID. -TEST_CASE("BPF_PROG_BIND_MAP etc.", "[libbpf]") +TEST_CASE("BPF_PROG_BIND_MAP etc.", "[libbpf][bpf]") { _test_helper_libbpf test_helper; test_helper.initialize(); @@ -2944,7 +2961,7 @@ TEST_CASE("BPF_PROG_BIND_MAP etc.", "[libbpf]") REQUIRE(program_fd >= 0); // Now query the program info and verify it matches what we set. - bpf_prog_info program_info; + sys_bpf_prog_info_t program_info = {}; memset(&attr, 0, sizeof(attr)); attr.info.bpf_fd = program_fd; attr.info.info = (uintptr_t)&program_info; @@ -2953,6 +2970,7 @@ TEST_CASE("BPF_PROG_BIND_MAP etc.", "[libbpf]") REQUIRE(attr.info.info_len == sizeof(program_info)); REQUIRE(program_info.nr_map_ids == 0); REQUIRE(program_info.type == BPF_PROG_TYPE_SAMPLE); + REQUIRE(strnlen(program_info.name, sizeof(program_info.name)) == 0); // Verify we can enumerate the program id. memset(&attr, 0, sizeof(attr)); @@ -2978,13 +2996,13 @@ TEST_CASE("BPF_PROG_BIND_MAP etc.", "[libbpf]") REQUIRE(map_fd > 0); // Query the map id. - bpf_map_info info; + sys_bpf_map_info_t map_info = {}; memset(&attr, 0, sizeof(attr)); attr.info.bpf_fd = map_fd; - attr.info.info = (uintptr_t)&info; - attr.info.info_len = sizeof(info); + attr.info.info = (uintptr_t)&map_info; + attr.info.info_len = sizeof(map_info); REQUIRE(bpf(BPF_OBJ_GET_INFO_BY_FD, &attr, sizeof(attr)) == 0); - ebpf_id_t map_id = info.id; + ebpf_id_t map_id = map_info.id; // Verify we can enumerate the map id. memset(&attr, 0, sizeof(attr)); @@ -3071,7 +3089,7 @@ TEST_CASE("BPF_PROG_BIND_MAP etc.", "[libbpf]") // Test bpf() with the following command ids: // BPF_PROG_ATTACH, BPF_PROG_DETACH -TEST_CASE("BPF_PROG_ATTACH", "[libbpf]") +TEST_CASE("BPF_PROG_ATTACH", "[libbpf][bpf]") { _test_helper_libbpf test_helper; test_helper.initialize(); @@ -3123,7 +3141,7 @@ TEST_CASE("BPF_PROG_ATTACH", "[libbpf]") // Test bpf() with the following command ids: // BPF_MAP_CREATE, BPF_MAP_UPDATE_ELEM, BPF_MAP_LOOKUP_ELEM, // BPF_MAP_GET_NEXT_KEY, BPF_MAP_LOOKUP_AND_DELETE_ELEM, and BPF_MAP_DELETE_ELEM. -TEST_CASE("BPF_MAP_GET_NEXT_KEY etc.", "[libbpf]") +TEST_CASE("BPF_MAP_GET_NEXT_KEY etc.", "[libbpf][bpf]") { _test_helper_libbpf test_helper; test_helper.initialize(); @@ -3235,6 +3253,101 @@ TEST_CASE("BPF_MAP_GET_NEXT_KEY etc.", "[libbpf]") Platform::_close(map_fd); } +TEST_CASE("Map and program information", "[libbpf][bpf]") +{ + _test_helper_libbpf test_helper; + test_helper.initialize(); + + // Create a map. + sys_bpf_map_create_attr_t map_create = {}; + map_create.map_type = BPF_MAP_TYPE_ARRAY; + map_create.key_size = sizeof(uint32_t); + map_create.value_size = sizeof(uint32_t); + map_create.max_entries = 2; + strncpy_s(map_create.map_name, "testing", sizeof(map_create.map_name)); + int map_fd = bpf(BPF_MAP_CREATE, (union bpf_attr*)&map_create, sizeof(map_create)); + REQUIRE(map_fd > 0); + + // Retrieve a prefix of map info. + sys_bpf_map_info_t map_info = {}; + union bpf_attr attr = {}; + attr.info.bpf_fd = map_fd; + attr.info.info = (uintptr_t)&map_info; + attr.info.info_len = offsetof(map_info, key_size); + REQUIRE(bpf(BPF_OBJ_GET_INFO_BY_FD, &attr, sizeof(attr)) == 0); + REQUIRE(attr.info.info_len == offsetof(map_info, key_size)); + REQUIRE(map_info.type == map_create.map_type); + REQUIRE(map_info.id != 0); + REQUIRE(map_info.key_size == 0); + + // Retrieve the map info. + attr = {}; + attr.info.bpf_fd = map_fd; + attr.info.info = (uintptr_t)&map_info; + attr.info.info_len = sizeof(map_info); + REQUIRE(bpf(BPF_OBJ_GET_INFO_BY_FD, &attr, sizeof(attr)) == 0); + REQUIRE(attr.info.info_len == sizeof(map_info)); + REQUIRE(map_info.type == map_create.map_type); + REQUIRE(map_info.id != 0); + REQUIRE(map_info.key_size == map_create.key_size); + REQUIRE(map_info.value_size == map_create.value_size); + REQUIRE(map_info.max_entries == map_create.max_entries); + REQUIRE(map_info.map_flags == map_create.map_flags); + REQUIRE(strncmp(map_info.name, map_create.map_name, sizeof(map_info.name)) == 0); + +#if !defined(CONFIG_BPF_JIT_DISABLED) + struct ebpf_inst instructions[] = { + {INST_ALU_OP_MOV | INST_CLS_ALU64, R0_RETURN_VALUE, 0}, // r0 = 0 + {INST_OP_EXIT}, // return r0 + }; + + // Load and verify the eBPF program. + attr = {}; + attr.prog_load.prog_type = BPF_PROG_TYPE_SAMPLE; + attr.prog_load.insns = (uintptr_t)instructions; + attr.prog_load.insn_cnt = _countof(instructions); + strncpy_s(attr.prog_load.prog_name, "testing", sizeof(attr.prog_load.prog_name)); + int program_fd = bpf(BPF_PROG_LOAD, &attr, sizeof(attr)); + REQUIRE(program_fd >= 0); + + // Bind map to the program so that the ID is returned in the info. + attr = {}; + attr.prog_bind_map.prog_fd = program_fd; + attr.prog_bind_map.map_fd = map_fd; + REQUIRE(bpf(BPF_PROG_BIND_MAP, &attr, sizeof(attr)) == 0); + + // Verify that we can query a prefix of fields. + sys_bpf_prog_info_t program_info = {}; + attr = {}; + attr.info.bpf_fd = program_fd; + attr.info.info = (uintptr_t)&program_info; + attr.info.info_len = offsetof(program_info, tag); + REQUIRE(bpf(BPF_OBJ_GET_INFO_BY_FD, &attr, sizeof(attr)) == 0); + REQUIRE(attr.info.info_len == offsetof(program_info, tag)); + REQUIRE(program_info.id != 0); + REQUIRE(program_info.nr_map_ids == 0); + REQUIRE(strnlen(program_info.name, sizeof(program_info.name)) == 0); + + // Query the full program info. + ebpf_id_t map_ids[2] = {}; + attr.info.info_len = sizeof(program_info); + program_info.nr_map_ids = sizeof(map_ids) / sizeof(map_ids[0]); + program_info.map_ids = (uintptr_t)map_ids; + REQUIRE(bpf(BPF_OBJ_GET_INFO_BY_FD, &attr, sizeof(attr)) == 0); + REQUIRE(attr.info.info_len == sizeof(program_info)); + REQUIRE(program_info.tag[0] == 0); + REQUIRE(program_info.jited_prog_len == 0); + REQUIRE(program_info.xlated_prog_len == 0); + REQUIRE(program_info.jited_prog_insns == 0); + REQUIRE(program_info.xlated_prog_insns == 0); + REQUIRE(program_info.load_time == 0); + REQUIRE(program_info.created_by_uid == 0); + REQUIRE(program_info.nr_map_ids == 1); + REQUIRE(map_ids[0] == map_info.id); + REQUIRE(strncmp(program_info.name, "testing", sizeof(program_info.name)) == 0); +#endif +} + TEST_CASE("libbpf_num_possible_cpus", "[libbpf]") { int cpu_count = libbpf_num_possible_cpus();