From 20d7a6565eaa63aabfd583b56e96e4c3285b78d5 Mon Sep 17 00:00:00 2001 From: Dave Thaler Date: Thu, 23 Sep 2021 19:32:09 -0700 Subject: [PATCH] Add more libbpf APIs needed by bpftool (#576) * Add more libbpf APIs needed by bpftool * Add missing export for existing bpf_map__name API * Add bpf_object__load * Add bpf_object__load_xattr * Add bpf_object__open_file * Add bpf_object__unload() API * Add bpf_program__get_type * Add bpf_program__set_type * Add bpf_program__unload() API * Add libbpf_get_error * Add libbpf_num_possible_cpus * Rename BPF_MAP_TYPE_UNSPECIFIED to BPF_MAP_TYPE_UNSPEC for libbpf compat * Rename BPF_PROG_TYPE_UNKNOWN to BPF_PROG_TYPE_UNSPEC for libbpf compat * Add attach_type to bpf_link_info * Add map_flags to bpf_map_info (but currently always 0) Fixes #575 Signed-off-by: Dave Thaler --- ebpfapi/Source.def | 10 + include/{ => bpf}/bpf.h | 2 +- include/{ => bpf}/libbpf.h | 122 ++++++++ include/ebpf_api.h | 10 +- include/ebpf_structs.h | 44 ++- include/linux/bpf.h | 8 +- libs/api/api.vcxproj | 1 + libs/api/api.vcxproj.filters | 3 + libs/api/api_internal.h | 72 ++++- libs/api/ebpf_api.cpp | 313 ++++++++++++++------ libs/api/libbpf_internal.h | 7 + libs/api/libbpf_object.cpp | 45 ++- libs/api/libbpf_program.cpp | 35 +++ libs/api/libbpf_system.cpp | 30 ++ libs/api_common/windows_platform_common.cpp | 2 +- libs/ebpfnetsh/links.cpp | 2 +- libs/ebpfnetsh/maps.cpp | 2 +- libs/ebpfnetsh/pins.cpp | 2 +- libs/ebpfnetsh/programs.cpp | 4 +- libs/execution_context/ebpf_link.c | 2 + libs/execution_context/ebpf_program.c | 2 +- rpc_interface/rpc_interface.idl | 4 +- tests/api_test/api_test.cpp | 2 +- tests/end_to_end/end_to_end.cpp | 4 +- tests/end_to_end/netsh_test.cpp | 6 +- tests/end_to_end/netsh_test_helper.h | 2 +- tests/libs/common/common_tests.h | 4 +- tests/libs/util/netsh_helper.cpp | 4 +- tests/libs/util/program_helper.h | 4 +- tests/performance/ExecutionContext.cpp | 4 +- tests/performance/performance.h | 2 +- tests/sample/ext/app/sample_ext_app.cpp | 6 +- tests/unit/libbpf_test.cpp | 54 +++- tools/port_quota/port_quota.cpp | 4 +- 34 files changed, 675 insertions(+), 143 deletions(-) rename include/{ => bpf}/bpf.h (99%) rename include/{ => bpf}/libbpf.h (84%) create mode 100644 libs/api/libbpf_system.cpp diff --git a/ebpfapi/Source.def b/ebpfapi/Source.def index eb2d39367..dc57d4118 100644 --- a/ebpfapi/Source.def +++ b/ebpfapi/Source.def @@ -19,6 +19,7 @@ EXPORTS bpf_map__is_pinned bpf_map__key_size bpf_map__max_entries + bpf_map__name bpf_map__next bpf_map__pin bpf_map__prev @@ -38,11 +39,15 @@ EXPORTS bpf_object__find_map_by_name bpf_object__find_map_fd_by_name bpf_object__find_program_by_name + bpf_object__load + bpf_object__load_xattr bpf_object__name bpf_object__next + bpf_object__open_file bpf_object__pin bpf_object__pin_maps bpf_object__pin_programs + bpf_object__unload bpf_object__unpin_maps bpf_object__unpin_programs bpf_prog_get_fd_by_id @@ -52,12 +57,15 @@ EXPORTS bpf_program__attach_xdp bpf_program__fd bpf_program__get_expected_attach_type + bpf_program__get_type bpf_program__name bpf_program__next bpf_program__pin bpf_program__prev bpf_program__section_name bpf_program__set_expected_attach_type + bpf_program__set_type + bpf_program__unload bpf_program__unpin ebpf_api_initiate ebpf_api_terminate @@ -84,4 +92,6 @@ EXPORTS ebpf_program_attach_by_fd ebpf_program_load ebpf_program_query_info + libbpf_get_error + libbpf_num_possible_cpus libbpf_prog_type_by_name diff --git a/include/bpf.h b/include/bpf/bpf.h similarity index 99% rename from include/bpf.h rename to include/bpf/bpf.h index 915e9f59d..a553bd238 100644 --- a/include/bpf.h +++ b/include/bpf/bpf.h @@ -199,7 +199,7 @@ bpf_map_update_elem(int fd, const void* key, const void* value, __u64 flags); /** * @brief Get a file descriptor for a pinned object by pin path. - * @param[in] path Pin path for the object. + * @param[in] pathname Pin path for the object. * * @return file descriptor for the pinned object, or -1 if not found. */ diff --git a/include/libbpf.h b/include/bpf/libbpf.h similarity index 84% rename from include/libbpf.h rename to include/bpf/libbpf.h index 12b41a758..748e3ca24 100644 --- a/include/libbpf.h +++ b/include/bpf/libbpf.h @@ -267,6 +267,58 @@ bpf_object__find_map_fd_by_name(const struct bpf_object* obj, const char* name); struct bpf_program* bpf_object__find_program_by_name(const struct bpf_object* obj, const char* name); +/** + * @brief Load all the programs in a given object. + * + * @param[in] obj Object from which to load programs. + * + * @retval 0 The operation was successful. + * @retval <0 An error occured, and errno was set. + * + * @exception EINVAL An invalid argument was provided. + * @exception ENOMEM Out of memory. + * + * @sa bpf_object__load_xattr + * @sa bpf_object__open + * @sa bpf_object__unload + * @sa bpf_prog_load + */ +int +bpf_object__load(struct bpf_object* obj); + +/** + * @brief Unload all the programs in a given object. + * + * @param[in] obj Object in which to unload programs. + * + * @retval 0 The operation was successful. + * @retval <0 An error occured, and errno was set. + * + * @sa bpf_object__load + * @sa bpf_object__load_xattr + * @sa bpf_prog_load + */ +int +bpf_object__unload(struct bpf_object* obj); + +/** + * @brief Load all the programs in a given object. + * + * @param[in] attr Structure with load attributes. + * + * @retval 0 The operation was successful. + * @retval <0 An error occured, and errno was set. + * + * @exception EINVAL An invalid argument was provided. + * @exception ENOMEM Out of memory. + * + * @sa bpf_object__open + * @sa bpf_object__load_xattr + * @sa bpf_prog_load + */ +int +bpf_object__load_xattr(struct bpf_object_load_attr* attr); + /** * @brief Get the name of an eBPF object. * @@ -287,6 +339,17 @@ bpf_object__name(const struct bpf_object* obj); struct bpf_object* bpf_object__next(struct bpf_object* prev); +/** + * @brief Open a file without loading the programs. + * + * @param[in] path File name to open. + * @param[opts] opts Options to use when opening the object. + * + * @returns Pointer to an eBPF object, or NULL on failure. + */ +struct bpf_object* +bpf_object__open_file(const char* path, const struct bpf_object_open_opts* opts); + /** * @brief Pin an eBPF object to a specified path. * @@ -468,6 +531,18 @@ bpf_program__fd(const struct bpf_program* prog); enum bpf_attach_type bpf_program__get_expected_attach_type(const struct bpf_program* prog); +/** + * @brief Get the program type for an eBPF program. + * + * @param[in] prog Program to check. + * + * @returns Program type. + * + * @sa bpf_program__get_expected_attach_type + */ +enum bpf_prog_type +bpf_program__get_type(const struct bpf_program* prog); + /** * @brief Get the function name of an eBPF program. * @@ -550,6 +625,28 @@ bpf_program__section_name(const struct bpf_program* prog); void bpf_program__set_expected_attach_type(struct bpf_program* prog, enum bpf_attach_type type); +/** + * @brief Set the program type for an eBPF program. + * + * @param[in] prog Program to update. + * @param[in] type Program type to set. + * + * @sa bpf_program__set_expected_attach_type + */ +void +bpf_program__set_type(struct bpf_program* prog, enum bpf_prog_type type); + +/** + * @brief Unload a program. + * + * @param[in] prog Program to unload. + * + * @sa bpf_object__unload + * @sa bpf_prog_load + */ +void +bpf_program__unload(struct bpf_program* prog); + /** * @brief Unpin a program. * @@ -583,6 +680,31 @@ libbpf_prog_type_by_name(const char* name, enum bpf_prog_type* prog_type, enum b /** @} */ +/** + * @name System-related functions + * @{ + */ + +/** + * @brief Get a negative error code based on errno and a possibly null pointer. + * + * @param[in] ptr Pointer that may be NULL. + * + * @returns Negative error code. + */ +long +libbpf_get_error(const void* ptr); + +/** + * @brief Get the number of processors on the current system. + * + * @returns Number of processors. + */ +int +libbpf_num_possible_cpus(void); + +/** @} */ + #else #pragma warning(push) #pragma warning(disable : 4200) // Zero-sized array in struct/union diff --git a/include/ebpf_api.h b/include/ebpf_api.h index 4ab05ce1d..3ab46f4d7 100644 --- a/include/ebpf_api.h +++ b/include/ebpf_api.h @@ -33,7 +33,7 @@ extern "C" __declspec(selectany) ebpf_attach_type_t EBPF_ATTACH_TYPE_SAMPLE = { 0xf788ef4b, 0x207d, 0x4dc3, {0x85, 0xcf, 0x0f, 0x2e, 0xa1, 0x07, 0x21, 0x3c}}; - __declspec(selectany) ebpf_attach_type_t EBPF_PROGRAM_TYPE_UNSPECIFIED = {0}; + __declspec(selectany) ebpf_program_type_t EBPF_PROGRAM_TYPE_UNSPECIFIED = {0}; /** @brief Program type for handling incoming packets as early as possible. * @@ -61,7 +61,7 @@ extern "C" 0xf788ef4a, 0x207d, 0x4dc3, {0x85, 0xcf, 0x0f, 0x2e, 0xa1, 0x07, 0x21, 0x3c}}; typedef int32_t fd_t; - const fd_t ebpf_fd_invalid = -1; + extern __declspec(selectany) const fd_t ebpf_fd_invalid = -1; typedef intptr_t ebpf_handle_t; typedef struct _tlv_type_length_value tlv_type_length_value_t; @@ -366,10 +366,10 @@ extern "C" * EBPF_EXECUTION_ANY is specified, execution type will be decided by a * system-wide policy. * @param[out] object Returns pointer to ebpf_object object. The caller - is expected to call ebpf_object_close() at the end. + is expected to call bpf_object__close() at the end. * @param[out] program_fd Returns a file descriptor for the first program. * The caller should not call _close() on the fd, but should instead use - * ebpf_object_close() to close this (and other) file descriptors. + * bpf_object__close() to close this (and other) file descriptors. * @param[out] log_buffer Returns a pointer to a null-terminated log buffer. * The caller is responsible for freeing the returned log_buffer pointer * by calling ebpf_free_string(). @@ -479,7 +479,7 @@ extern "C" * @brief Get a program type and expected attach type by name. * * @param[in] name Name, as if it were a section name in an ELF file. - * @param[out] prog_type Program type. + * @param[out] program_type Program type. * @param[out] expected_attach_type Expected attach type. * * @retval EBPF_SUCCESS The operation was successful. diff --git a/include/ebpf_structs.h b/include/ebpf_structs.h index 5a21a8cb5..063f096cd 100644 --- a/include/ebpf_structs.h +++ b/include/ebpf_structs.h @@ -12,9 +12,9 @@ typedef enum bpf_map_type { - BPF_MAP_TYPE_UNSPECIFIED = 0, ///< Unspecified map type. - BPF_MAP_TYPE_HASH = 1, ///< Hash table. - BPF_MAP_TYPE_ARRAY = 2, ///< Array, where the map key is the array index. + BPF_MAP_TYPE_UNSPEC = 0, ///< Unspecified map type. + BPF_MAP_TYPE_HASH = 1, ///< Hash table. + BPF_MAP_TYPE_ARRAY = 2, ///< Array, where the map key is the array index. BPF_MAP_TYPE_PROG_ARRAY = 3, ///< Array of program fds usable with bpf_tail_call, where the map key is the array index. BPF_MAP_TYPE_PERCPU_HASH = 4, @@ -84,20 +84,47 @@ typedef enum // Cross-platform BPF program types. enum bpf_prog_type { - BPF_PROG_TYPE_UNKNOWN, + BPF_PROG_TYPE_UNSPEC, BPF_PROG_TYPE_XDP, BPF_PROG_TYPE_BIND, // TODO(#333): replace with cross-platform program type }; +// The link type is used to tell which union member is present +// in the bpf_link_info struct. There is exactly one non-zero value +// per union member. +enum bpf_link_type +{ + BPF_LINK_TYPE_UNSPEC, + BPF_LINK_TYPE_PLAIN, +}; + +enum bpf_attach_type +{ + BPF_ATTACH_TYPE_UNSPEC, + BPF_ATTACH_TYPE_XDP, + __MAX_BPF_ATTACH_TYPE, +}; + // Libbpf itself requires the following structs to be defined, but doesn't // care what fields they have. Applications such as bpftool on the other // hand depend on fields of specific names and types. +#pragma warning(push) +#pragma warning(disable : 4201) /* nameless struct/union */ struct bpf_link_info { - ebpf_id_t id; ///< Link ID. - ebpf_id_t prog_id; ///< Program ID. + ebpf_id_t id; ///< Link ID. + ebpf_id_t prog_id; ///< Program ID. + enum bpf_link_type type; ///< Link type. + union + { + struct + { + int attach_type; ///< Attach type integer. + }; + }; }; +#pragma warning(pop) #define BPF_OBJ_NAME_LEN 64 @@ -110,12 +137,17 @@ struct bpf_map_info uint32_t value_size; ///< Size in bytes of a map value. uint32_t max_entries; ///< Maximum number of entries allowed in the map. char name[BPF_OBJ_NAME_LEN]; ///< Null-terminated map name. + uint32_t map_flags; ///< Map flags. // Windows-specific fields. ebpf_id_t inner_map_id; ///< ID of inner map template. uint32_t pinned_path_count; ///< Number of pinned paths. }; +#define BPF_ANY 0x0 +#define BPF_NOEXIST 0x1 +#define BPF_EXIST 0x2 + struct bpf_prog_info { // Cross-platform fields. diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 9683b7ee1..1ed1110d6 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -13,17 +13,13 @@ #undef LIBBPF_DEPRECATED #define LIBBPF_DEPRECATED(x) +typedef int32_t __s32; + typedef uint8_t __u8; typedef uint32_t __u32; typedef uint64_t __u64; typedef uint32_t pid_t; -enum bpf_attach_type -{ - BPF_ATTACH_TYPE_UNSPEC, - BPF_ATTACH_TYPE_XDP, -}; - enum bpf_func_id { BPF_FUNC_ID_UNKNOWN diff --git a/libs/api/api.vcxproj b/libs/api/api.vcxproj index 16008ac34..ee6aac4f7 100644 --- a/libs/api/api.vcxproj +++ b/libs/api/api.vcxproj @@ -178,6 +178,7 @@ + diff --git a/libs/api/api.vcxproj.filters b/libs/api/api.vcxproj.filters index 95a10ba97..5bab81ee3 100644 --- a/libs/api/api.vcxproj.filters +++ b/libs/api/api.vcxproj.filters @@ -37,6 +37,9 @@ Source Files + + Source Files + diff --git a/libs/api/api_internal.h b/libs/api/api_internal.h index d1349bc01..8d39df7c5 100644 --- a/libs/api/api_internal.h +++ b/libs/api/api_internal.h @@ -61,9 +61,10 @@ typedef struct bpf_link typedef struct bpf_object { - char* file_name = nullptr; + char* object_name = nullptr; std::vector programs; std::vector maps; + bool loaded = false; } ebpf_object_t; ebpf_result_t @@ -120,6 +121,17 @@ ebpf_program_next(_In_opt_ const struct bpf_program* previous, _In_ const struct _Ret_maybenull_ struct bpf_program* ebpf_program_previous(_In_opt_ const struct bpf_program* next, _In_ const struct bpf_object* object); +/** + * @brief Unload an eBPF program. + * + * @param[in] program Program to unload. + * + * @retval EBPF_SUCCESS The operation was successful. + * @retval EBPF_INVALID_ARGUMENT One or more parameters are wrong. + */ +ebpf_result_t +ebpf_program_unload(_In_ struct bpf_program* program); + /** * @brief Get next map in ebpf_object object. * @@ -179,7 +191,7 @@ ebpf_map_get_fd(_In_ const struct bpf_map* map); * @param[in] object Pointer to ebpf_object. */ void -ebpf_object_close(_In_ _Post_invalid_ struct bpf_object* object); +ebpf_object_close(_In_opt_ _Post_invalid_ struct bpf_object* object); void initialize_map(_Out_ ebpf_map_t* map, _In_ const map_cache_t& map_cache); @@ -391,3 +403,59 @@ ebpf_object_pin(fd_t fd, _In_z_ const char* path); */ fd_t ebpf_object_get(_In_z_ const char* path); + +/** + * @brief Open a file without loading the programs. + * + * @param[in] path File name to open. + * @param[in] object_name Optional object name to override file name + * as the object name. + * @param[in] program_type Optional program type for all programs. + * If NULL, the program type is derived from the section names. + * @param[in] attach_type Default attach type for all programs. + * If NULL, the attach type is derived from the section names. + * @param[out] object Returns a pointer to the object created. + * @param[out] error_message Error message string, which + * the caller must free using ebpf_free_string(). + * + * @retval EBPF_SUCCESS The operation was successful. + * @retval EBPF_INVALID_ARGUMENT One or more parameters are wrong. + * @retval EBPF_NO_MEMORY Out of memory. + */ +ebpf_result_t +ebpf_object_open( + _In_z_ const char* path, + _In_opt_z_ const char* object_name, + _In_opt_ const ebpf_program_type_t* program_type, + _In_opt_ const ebpf_attach_type_t* attach_type, + _Outptr_ struct bpf_object** object, + _Outptr_result_maybenull_z_ const char** error_message) noexcept; + +/** + * @brief Load all the programs in a given object. + * + * @param[in] object Object from which to load programs. + * @param[in] execution_type Execution type. + * @param[out] error_message Error message string, which + * the caller must free using ebpf_free_string(). + * + * @retval EBPF_SUCCESS The operation was successful. + * @retval EBPF_INVALID_ARGUMENT One or more parameters are wrong. + * @retval EBPF_NO_MEMORY Out of memory. + */ +ebpf_result_t +ebpf_object_load( + _Inout_ struct bpf_object* object, + ebpf_execution_type_t execution_type, + _Outptr_result_maybenull_z_ const char** error_message); + +/** + * @brief Unload all the programs in a given object. + * + * @param[in] object Object in which to unload programs. + * + * @retval EBPF_SUCCESS The operation was successful. + * @retval EBPF_INVALID_ARGUMENT One or more parameters are wrong. + */ +ebpf_result_t +ebpf_object_unload(_In_ struct bpf_object* object); diff --git a/libs/api/ebpf_api.cpp b/libs/api/ebpf_api.cpp index 52484d33b..717d4494c 100644 --- a/libs/api/ebpf_api.cpp +++ b/libs/api/ebpf_api.cpp @@ -287,7 +287,7 @@ _get_map_descriptor_properties( ebpf_result_t result = EBPF_SUCCESS; ebpf_map_t* map; - *type = BPF_MAP_TYPE_UNSPECIFIED; + *type = BPF_MAP_TYPE_UNSPEC; *key_size = 0; *value_size = 0; @@ -1101,7 +1101,10 @@ ebpf_program_attach( } else { program_attach_type = &program->attach_type; } - assert(program->handle != ebpf_handle_invalid); + if (program->handle == ebpf_handle_invalid) { + result = EBPF_INVALID_ARGUMENT; + goto Exit; + } result = _link_ebpf_program(program->handle, program_attach_type, link, (uint8_t*)attach_parameters, attach_params_size); @@ -1285,12 +1288,8 @@ clean_up_ebpf_program(_In_ _Post_invalid_ ebpf_program_t* program) if (program == nullptr) { return; } - if (program->fd != ebpf_fd_invalid) { - Platform::_close(program->fd); - } - if (program->handle != ebpf_handle_invalid) { - _ebpf_programs.erase(program->handle); - } + ebpf_program_unload(program); + free(program->byte_code); free(program->program_name); free(program->section_name); @@ -1313,7 +1312,7 @@ clean_up_ebpf_map(_In_ _Post_invalid_ ebpf_map_t* map) if (map == nullptr) { return; } - if (map->map_fd != 0) { + if (map->map_fd > 0) { Platform::_close(map->map_fd); } if (map->map_handle != ebpf_handle_invalid) { @@ -1392,6 +1391,7 @@ initialize_map(_Out_ ebpf_map_t* map, _In_ const map_cache_t& map_cache) static ebpf_result_t _initialize_ebpf_object_from_elf( _In_z_ const char* file_name, + _In_opt_z_ const char* object_name, _In_opt_ const ebpf_program_type_t* expected_program_type, _In_opt_ const ebpf_attach_type_t* expected_attach_type, _Out_ ebpf_object_t& object, @@ -1406,16 +1406,18 @@ _initialize_ebpf_object_from_elf( goto Exit; } - object.file_name = _strdup(file_name); - if (object.file_name == nullptr) { + object.object_name = _strdup(object_name ? object_name : file_name); + if (object.object_name == nullptr) { result = EBPF_NO_MEMORY; goto Exit; } for (auto& program : object.programs) { + program->fd = ebpf_fd_invalid; program->object = &object; } for (auto& map : object.maps) { + map->map_fd = ebpf_fd_invalid; map->object = &object; } @@ -1475,6 +1477,214 @@ _get_next_map_to_create(std::vector& maps) return nullptr; } +ebpf_result_t +ebpf_object_open( + _In_z_ const char* path, + _In_opt_z_ const char* object_name, + _In_opt_ const ebpf_program_type_t* program_type, + _In_opt_ const ebpf_attach_type_t* attach_type, + _Outptr_ struct bpf_object** object, + _Outptr_result_maybenull_z_ const char** error_message) noexcept +{ + *error_message = nullptr; + + ebpf_object_t* new_object = new (std::nothrow) ebpf_object_t(); + if (new_object == nullptr) { + return EBPF_NO_MEMORY; + } + + ebpf_result_t result = + _initialize_ebpf_object_from_elf(path, object_name, program_type, attach_type, *new_object, error_message); + if (result != EBPF_SUCCESS) { + goto Done; + } + + *object = new_object; + _ebpf_objects.emplace_back(*object); + +Done: + clear_map_descriptors(); + if (result != EBPF_SUCCESS) { + _clean_up_ebpf_object(new_object); + } + return result; +} + +static ebpf_result_t +_ebpf_object_create_maps(_Inout_ ebpf_object_t* object) +{ + ebpf_result_t result = EBPF_SUCCESS; + + clear_map_descriptors(); + + // TODO: update ebpf_map_definition_t structure so that it contains flag and pinning information. + for (int count = 0; count < object->maps.size(); count++) { + ebpf_map_t* map = _get_next_map_to_create(object->maps); + if (map == nullptr) { + // Any remaining maps cannot be created. + result = EBPF_INVALID_OBJECT; + break; + } + + ebpf_handle_t inner_map_handle = (map->inner_map) ? map->inner_map->map_handle : ebpf_handle_invalid; + result = _create_map(map->name, &map->map_definition, inner_map_handle, &map->map_handle); + if (result != EBPF_SUCCESS) { + break; + } + map->map_fd = _create_file_descriptor_for_handle(map->map_handle); + } + + if (result != EBPF_SUCCESS) { + clean_up_ebpf_maps(object->maps); + } else { + for (auto& map : object->maps) { + _ebpf_maps.insert(std::pair(map->map_handle, map)); + } + } + clear_map_descriptors(); + return result; +} + +static ebpf_result_t +_ebpf_object_load_programs( + _Inout_ struct bpf_object* object, + _In_ ebpf_execution_type_t execution_type, + _Outptr_result_maybenull_z_ const char** log_buffer) +{ + ebpf_result_t result = EBPF_SUCCESS; + std::vector handle_map; + uint32_t error_message_size = 0; + + *log_buffer = nullptr; + + for (auto& program : object->programs) { + result = _create_program( + program->program_type, object->object_name, program->section_name, program->program_name, &program->handle); + if (result != EBPF_SUCCESS) { + break; + } + + program->fd = _create_file_descriptor_for_handle(program->handle); + + // Populate load_info. + ebpf_program_load_info load_info = {0}; + load_info.object_name = const_cast(object->object_name); + load_info.section_name = const_cast(program->section_name); + load_info.program_name = const_cast(program->program_name); + load_info.program_type = program->program_type; + load_info.program_handle = reinterpret_cast(program->handle); + load_info.execution_type = execution_type; + load_info.byte_code = program->byte_code; + load_info.byte_code_size = program->byte_code_size; + load_info.execution_context = execution_context_kernel_mode; + load_info.map_count = (uint32_t)object->maps.size(); + + if (load_info.map_count > 0) { + for (auto& map : object->maps) { + fd_t inner_map_original_fd = (map->inner_map) ? map->inner_map->original_fd : ebpf_fd_invalid; + handle_map.emplace_back( + map->original_fd, inner_map_original_fd, reinterpret_cast(map->map_handle)); + } + + load_info.handle_map = handle_map.data(); + } + + result = ebpf_rpc_load_program(&load_info, log_buffer, &error_message_size); + if (result != EBPF_SUCCESS) { + break; + } + } + + if (result == EBPF_SUCCESS) { + for (auto& program : object->programs) { + _ebpf_programs.insert(std::pair(program->handle, program)); + } + } + return result; +} + +// This logic is intended to be similar to libbpf's bpf_object__load_xattr(). +ebpf_result_t +ebpf_object_load( + _Inout_ struct bpf_object* object, + ebpf_execution_type_t execution_type, + _Outptr_result_maybenull_z_ const char** log_buffer) +{ + if (!object) + return EBPF_INVALID_ARGUMENT; + + if (object->loaded) { + return EBPF_INVALID_ARGUMENT; + } + + ebpf_result_t result = EBPF_SUCCESS; + try { + result = _ebpf_object_create_maps(object); + if (result != EBPF_SUCCESS) { + goto Done; + } + + result = _ebpf_object_load_programs(object, execution_type, log_buffer); + } catch (const std::bad_alloc&) { + result = EBPF_NO_MEMORY; + goto Done; + } catch (...) { + result = EBPF_FAILED; + goto Done; + } + + object->loaded = true; /* doesn't matter if successfully loaded or not */ + +Done: + if (result != EBPF_SUCCESS) { + ebpf_object_unload(object); + } + return result; +} + +// This function is intended to work like libbpf's bpf_object__unload(). +ebpf_result_t +ebpf_object_unload(_In_ struct bpf_object* object) +{ + if (!object) + return EBPF_INVALID_ARGUMENT; + + for (auto& map : object->maps) { + if (map->map_fd > 0) { + Platform::_close(map->map_fd); + map->map_fd = ebpf_fd_invalid; + } + if (map->map_handle != ebpf_handle_invalid) { + _ebpf_maps.erase(map->map_handle); + map->map_handle = ebpf_handle_invalid; + } + } + + for (auto& program : object->programs) { + ebpf_program_unload(program); + } + + return EBPF_SUCCESS; +} + +// This function is intended to work like libbpf's bpf_program__unload(). +ebpf_result_t +ebpf_program_unload(_In_ struct bpf_program* program) +{ + if (!program) + return EBPF_INVALID_ARGUMENT; + + if (program->fd != ebpf_fd_invalid) { + Platform::_close(program->fd); + program->fd = ebpf_fd_invalid; + } + if (program->handle != ebpf_handle_invalid) { + _ebpf_programs.erase(program->handle); + program->handle = ebpf_handle_invalid; + } + return EBPF_SUCCESS; +} + ebpf_result_t ebpf_program_load( _In_z_ const char* file_name, @@ -1487,11 +1697,8 @@ ebpf_program_load( { ebpf_object_t* new_object = nullptr; ebpf_protocol_buffer_t request_buffer; - uint32_t error_message_size = 0; std::vector handles; ebpf_result_t result = EBPF_SUCCESS; - ebpf_program_load_info load_info = {0}; - std::vector handle_map; if (file_name == nullptr || object == nullptr || program_fd == nullptr || log_buffer == nullptr) { result = EBPF_INVALID_ARGUMENT; @@ -1506,85 +1713,19 @@ ebpf_program_load( } clear_map_descriptors(); - *log_buffer = nullptr; - *object = nullptr; try { - new_object = new ebpf_object_t(); - if (new_object == nullptr) { - result = EBPF_NO_MEMORY; - goto Done; - } - - result = _initialize_ebpf_object_from_elf(file_name, program_type, attach_type, *new_object, log_buffer); + result = ebpf_object_open(file_name, nullptr, program_type, attach_type, &new_object, log_buffer); if (result != EBPF_SUCCESS) { goto Done; } - // Create all maps. - // TODO: update ebpf_map_definition_t structure so that it contains flag and pinning information. - for (int count = 0; count < new_object->maps.size(); count++) { - ebpf_map_t* map = _get_next_map_to_create(new_object->maps); - if (map == nullptr) { - // Any remaining maps cannot be created. - result = EBPF_INVALID_OBJECT; - goto Done; - } - - ebpf_handle_t inner_map_handle = (map->inner_map) ? map->inner_map->map_handle : ebpf_handle_invalid; - result = _create_map(map->name, &map->map_definition, inner_map_handle, &map->map_handle); - if (result != EBPF_SUCCESS) { - goto Done; - } - map->map_fd = _create_file_descriptor_for_handle(map->map_handle); - } - - for (auto& program : new_object->programs) { - result = _create_program( - program->program_type, file_name, program->section_name, program->program_name, &program->handle); - if (result != EBPF_SUCCESS) { - goto Done; - } - - program->fd = _create_file_descriptor_for_handle(program->handle); - - // populate load_info. - load_info.file_name = const_cast(file_name); - load_info.section_name = const_cast(program->section_name); - load_info.program_name = const_cast(program->program_name); - load_info.program_type = program->program_type; - load_info.program_handle = reinterpret_cast(program->handle); - load_info.execution_type = execution_type; - load_info.byte_code = program->byte_code; - load_info.byte_code_size = program->byte_code_size; - load_info.execution_context = execution_context_kernel_mode; - load_info.map_count = (uint32_t)new_object->maps.size(); - - if (load_info.map_count > 0) { - for (auto& map : new_object->maps) { - fd_t inner_map_original_fd = (map->inner_map) ? map->inner_map->original_fd : ebpf_fd_invalid; - handle_map.emplace_back( - map->original_fd, inner_map_original_fd, reinterpret_cast(map->map_handle)); - } - - load_info.handle_map = handle_map.data(); - } - - result = ebpf_rpc_load_program(&load_info, log_buffer, &error_message_size); - if (result != EBPF_SUCCESS) { - goto Done; - } - } - - for (auto& program : new_object->programs) { - _ebpf_programs.insert(std::pair(program->handle, program)); - } - for (auto& map : new_object->maps) { - _ebpf_maps.insert(std::pair(map->map_handle, map)); + result = ebpf_object_load(new_object, execution_type, log_buffer); + if (result != EBPF_SUCCESS) { + goto Done; } *object = new_object; - _ebpf_objects.emplace_back(*object); *program_fd = new_object->programs[0]->fd; } catch (const std::bad_alloc&) { result = EBPF_NO_MEMORY; @@ -1596,7 +1737,7 @@ ebpf_program_load( Done: if (result != EBPF_SUCCESS) { - _clean_up_ebpf_object(new_object); + ebpf_object_close(new_object); } clear_map_descriptors(); return result; @@ -1745,7 +1886,7 @@ ebpf_map_get_fd(_In_ const struct bpf_map* map) } void -ebpf_object_close(_In_ _Post_invalid_ struct bpf_object* object) +ebpf_object_close(_In_opt_ _Post_invalid_ struct bpf_object* object) { if (object == nullptr) { return; diff --git a/libs/api/libbpf_internal.h b/libs/api/libbpf_internal.h index 2d1404b75..8c20fdac4 100644 --- a/libs/api/libbpf_internal.h +++ b/libs/api/libbpf_internal.h @@ -18,3 +18,10 @@ libbpf_result_err(ebpf_result_t result) { return libbpf_err(-ebpf_result_to_errno(result)); } + +static inline void* +libbpf_err_ptr(int err) +{ + errno = -err; + return NULL; +} diff --git a/libs/api/libbpf_object.cpp b/libs/api/libbpf_object.cpp index 85e7f4c8d..277f22e07 100644 --- a/libs/api/libbpf_object.cpp +++ b/libs/api/libbpf_object.cpp @@ -17,7 +17,7 @@ const char* bpf_object__name(const struct bpf_object* object) { - return object->file_name; + return object->object_name; } int @@ -54,8 +54,7 @@ bpf_object__find_program_by_name(const struct bpf_object* obj, const char* name) if (!strcmp(prog->program_name, name)) return prog; } - errno = ENOENT; - return nullptr; + return (struct bpf_program*)libbpf_err_ptr(-ENOENT); } struct bpf_object* @@ -83,3 +82,43 @@ bpf_obj_get(const char* pathname) { return (int)ebpf_object_get(pathname); } + +struct bpf_object* +bpf_object__open_file(const char* path, const struct bpf_object_open_opts* opts) +{ + if (!path) { + return (struct bpf_object*)libbpf_err_ptr(-EINVAL); + } + + struct bpf_object* object; + const char* error_message; + ebpf_result_t result = ebpf_object_open(path, opts->object_name, nullptr, nullptr, &object, &error_message); + ebpf_free_string(error_message); + libbpf_result_err(result); // Set errno. + return object; +} + +int +bpf_object__load_xattr(struct bpf_object_load_attr* attr) +{ + const char* error_message; + ebpf_result result = ebpf_object_load(attr->obj, EBPF_EXECUTION_ANY, &error_message); + ebpf_free_string(error_message); + return libbpf_result_err(result); +} + +int +bpf_object__load(struct bpf_object* object) +{ + struct bpf_object_load_attr attr = { + .obj = object, + }; + + return bpf_object__load_xattr(&attr); +} + +int +bpf_object__unload(struct bpf_object* obj) +{ + return libbpf_result_err(ebpf_object_unload(obj)); +} diff --git a/libs/api/libbpf_program.cpp b/libs/api/libbpf_program.cpp index a575825cb..008b2bc2c 100644 --- a/libs/api/libbpf_program.cpp +++ b/libs/api/libbpf_program.cpp @@ -27,6 +27,19 @@ _get_ebpf_program_type(enum bpf_prog_type type) return nullptr; } +static enum bpf_prog_type +_get_bpf_program_type(const ebpf_program_type_t* type) +{ + // TODO(issue #223): read this mapping from the registry + if (memcmp(type, &EBPF_PROGRAM_TYPE_XDP, sizeof(*type)) == 0) { + return BPF_PROG_TYPE_XDP; + } + if (memcmp(type, &EBPF_PROGRAM_TYPE_BIND, sizeof(*type)) == 0) { + return BPF_PROG_TYPE_BIND; + } + return BPF_PROG_TYPE_UNSPEC; +} + static const ebpf_attach_type_t* _get_ebpf_attach_type(enum bpf_attach_type type) { @@ -271,6 +284,19 @@ bpf_program__set_expected_attach_type(struct bpf_program* program, enum bpf_atta program->attach_type = *_get_ebpf_attach_type(type); } +enum bpf_prog_type +bpf_program__get_type(const struct bpf_program* program) +{ + return _get_bpf_program_type(&program->program_type); +} + +void +bpf_program__set_type(struct bpf_program* program, enum bpf_prog_type type) +{ + const ebpf_program_type_t* program_type = _get_ebpf_program_type(type); + program->program_type = (program_type != nullptr) ? *program_type : EBPF_PROGRAM_TYPE_UNSPECIFIED; +} + int bpf_prog_get_fd_by_id(uint32_t id) { @@ -318,3 +344,12 @@ libbpf_prog_type_by_name(const char* name, enum bpf_prog_type* prog_type, enum b errno = ESRCH; return -1; } + +void +bpf_program__unload(struct bpf_program* prog) +{ + ebpf_result_t result = ebpf_program_unload(prog); + if (result != EBPF_SUCCESS) { + errno = ebpf_result_to_errno(result); + } +} diff --git a/libs/api/libbpf_system.cpp b/libs/api/libbpf_system.cpp new file mode 100644 index 000000000..e1e6127b1 --- /dev/null +++ b/libs/api/libbpf_system.cpp @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation +// SPDX-License-Identifier: MIT + +#include +#pragma warning(push) +#pragma warning(disable : 4200) +#include "libbpf.h" +#pragma warning(pop) + +long +libbpf_get_error(const void* ptr) +{ + /* Older versions of Linux encode error numbers in a + * void* in some cases, so libbpf has to deal with both + * that case and the simple case of using errno, so + * exposes this API to be agnostic. But we always use + * errno, so don't need to look at the ptr value. + */ + UNREFERENCED_PARAMETER(ptr); + + return -errno; +} + +int +libbpf_num_possible_cpus(void) +{ + SYSTEM_INFO info; + GetSystemInfo(&info); + return info.dwNumberOfProcessors; +} diff --git a/libs/api_common/windows_platform_common.cpp b/libs/api_common/windows_platform_common.cpp index 2671de367..b79e8fe19 100644 --- a/libs/api_common/windows_platform_common.cpp +++ b/libs/api_common/windows_platform_common.cpp @@ -120,7 +120,7 @@ get_program_type_windows(const std::string& section, const std::string&) #define BPF_MAP_TYPE(x) BPF_MAP_TYPE_##x, #x static const EbpfMapType windows_map_types[] = { - {BPF_MAP_TYPE(UNSPECIFIED)}, + {BPF_MAP_TYPE(UNSPEC)}, {BPF_MAP_TYPE(HASH)}, {BPF_MAP_TYPE(ARRAY), true}, {BPF_MAP_TYPE(PROG_ARRAY), true, EbpfMapValueType::PROGRAM}, diff --git a/libs/ebpfnetsh/links.cpp b/libs/ebpfnetsh/links.cpp index d9cb8c6e2..c711da5c1 100644 --- a/libs/ebpfnetsh/links.cpp +++ b/libs/ebpfnetsh/links.cpp @@ -9,7 +9,7 @@ #include #include #include -#include "bpf.h" +#include "bpf/bpf.h" #include "ebpf_api.h" #include "ebpf_windows.h" #include "platform.h" diff --git a/libs/ebpfnetsh/maps.cpp b/libs/ebpfnetsh/maps.cpp index 6adf476b3..044c650e7 100644 --- a/libs/ebpfnetsh/maps.cpp +++ b/libs/ebpfnetsh/maps.cpp @@ -9,7 +9,7 @@ #include #include #include -#include "bpf.h" +#include "bpf/bpf.h" #include "ebpf_api.h" #include "ebpf_windows.h" #include "platform.h" diff --git a/libs/ebpfnetsh/pins.cpp b/libs/ebpfnetsh/pins.cpp index 587327ed5..69c5ec839 100644 --- a/libs/ebpfnetsh/pins.cpp +++ b/libs/ebpfnetsh/pins.cpp @@ -10,7 +10,7 @@ #include #include #include -#include "bpf.h" +#include "bpf/bpf.h" #include "ebpf_api.h" #include "ebpf_windows.h" #include "pins.h" diff --git a/libs/ebpfnetsh/programs.cpp b/libs/ebpfnetsh/programs.cpp index b265a5c2c..5d746a59d 100644 --- a/libs/ebpfnetsh/programs.cpp +++ b/libs/ebpfnetsh/programs.cpp @@ -9,9 +9,9 @@ #include #include #include -#include "bpf.h" +#include "bpf/bpf.h" +#include "bpf/libbpf.h" #include "ebpf_api.h" -#include "libbpf.h" #include "platform.h" #include "programs.h" #include "tokens.h" diff --git a/libs/execution_context/ebpf_link.c b/libs/execution_context/ebpf_link.c index 7bfe1db24..ff7721725 100644 --- a/libs/execution_context/ebpf_link.c +++ b/libs/execution_context/ebpf_link.c @@ -188,6 +188,8 @@ ebpf_link_get_info( info->id = link->object.id; info->prog_id = (link->program) ? ((ebpf_object_t*)link->program)->id : EBPF_ID_NONE; + info->type = BPF_LINK_TYPE_PLAIN; + info->attach_type = BPF_ATTACH_TYPE_UNSPEC; // TODO(#223): get actual integer, and also return attach_type_uuid *info_size = sizeof(*info); return EBPF_SUCCESS; diff --git a/libs/execution_context/ebpf_program.c b/libs/execution_context/ebpf_program.c index b97979b8e..3c1a52e9b 100644 --- a/libs/execution_context/ebpf_program.c +++ b/libs/execution_context/ebpf_program.c @@ -872,7 +872,7 @@ ebpf_program_get_info( (char*)program->parameters.program_name.value, program->parameters.program_name.length); info->nr_map_ids = program->count_of_maps; - info->type = BPF_PROG_TYPE_UNKNOWN; // TODO(issue #223): get integer if any. + info->type = BPF_PROG_TYPE_UNSPEC; // TODO(issue #223): get integer if any. info->type_uuid = *ebpf_program_type(program); info->pinned_path_count = program->object.pinned_path_count; info->link_count = program->link_count; diff --git a/rpc_interface/rpc_interface.idl b/rpc_interface/rpc_interface.idl index 0b5df0387..03ddf2572 100644 --- a/rpc_interface/rpc_interface.idl +++ b/rpc_interface/rpc_interface.idl @@ -45,8 +45,8 @@ import "wtypes.idl"; typedef struct _ebpf_program_load_info { - // Optional file name. - [string] char* file_name; + // Object name. + [string] char* object_name; // Optional section name. [string] char* section_name; // Optional program name. diff --git a/tests/api_test/api_test.cpp b/tests/api_test/api_test.cpp index a5867eb31..51c0a84ae 100644 --- a/tests/api_test/api_test.cpp +++ b/tests/api_test/api_test.cpp @@ -7,11 +7,11 @@ #include #include #include "api_test.h" +#include "bpf/libbpf.h" #include "catch_wrapper.hpp" #include "common_tests.h" #include #include "service_helper.h" -#include "libbpf.h" #define SAMPLE_PATH "" #define EBPF_CORE_DRIVER_BINARY_NAME L"ebpfcore.sys" diff --git a/tests/end_to_end/end_to_end.cpp b/tests/end_to_end/end_to_end.cpp index 22237b57a..af8801c16 100644 --- a/tests/end_to_end/end_to_end.cpp +++ b/tests/end_to_end/end_to_end.cpp @@ -10,14 +10,14 @@ #include #include // Must come after Winsock2.h -#include "bpf.h" +#include "bpf/bpf.h" +#include "bpf/libbpf.h" #include "catch_wrapper.hpp" #include "common_tests.h" #include "ebpf_bind_program_data.h" #include "ebpf_core.h" #include "ebpf_xdp_program_data.h" #include "helpers.h" -#include "libbpf.h" #include "mock.h" #include "platform.h" #include "program_helper.h" diff --git a/tests/end_to_end/netsh_test.cpp b/tests/end_to_end/netsh_test.cpp index df8d194cc..27ae690fb 100644 --- a/tests/end_to_end/netsh_test.cpp +++ b/tests/end_to_end/netsh_test.cpp @@ -4,14 +4,14 @@ #include #include // Must be included after windows.h #include -#include "ebpf_epoch.h" +#include "bpf/bpf.h" #pragma warning(push) #pragma warning(disable : 4200) -#include "libbpf.h" +#include "bpf/libbpf.h" #pragma warning(pop) +#include "ebpf_epoch.h" #include "netsh_test_helper.h" #include "platform.h" -#include "programs.h" #include "test_helper.hpp" TEST_CASE("show disassembly bpf.o", "[netsh][disassembly]") diff --git a/tests/end_to_end/netsh_test_helper.h b/tests/end_to_end/netsh_test_helper.h index f3b456f8b..507405bf8 100644 --- a/tests/end_to_end/netsh_test_helper.h +++ b/tests/end_to_end/netsh_test_helper.h @@ -6,7 +6,7 @@ #include #include // Must be included after windows.h #include -#include "bpf.h" +#include "bpf/bpf.h" #include "capture_helper.hpp" #include "catch_wrapper.hpp" #include "elf.h" diff --git a/tests/libs/common/common_tests.h b/tests/libs/common/common_tests.h index ffc9c79b5..7bf337988 100644 --- a/tests/libs/common/common_tests.h +++ b/tests/libs/common/common_tests.h @@ -6,10 +6,10 @@ #pragma once #include -#include "bpf.h" +#include "bpf/bpf.h" +#include "bpf/libbpf.h" #include "ebpf_api.h" #include "ebpf_result.h" -#include "libbpf.h" void ebpf_test_pinned_map_enum(); diff --git a/tests/libs/util/netsh_helper.cpp b/tests/libs/util/netsh_helper.cpp index e24bfdedc..043607bf5 100644 --- a/tests/libs/util/netsh_helper.cpp +++ b/tests/libs/util/netsh_helper.cpp @@ -4,7 +4,7 @@ #include #include // Must be included after windows.h #include -#include "bpf.h" +#include "bpf/bpf.h" #include "capture_helper.hpp" #include "catch_wrapper.hpp" #include "elf.h" @@ -129,4 +129,4 @@ _run_netsh_command( *result = NO_ERROR; return capture.get_stdout_contents(); -} \ No newline at end of file +} diff --git a/tests/libs/util/program_helper.h b/tests/libs/util/program_helper.h index 2e2372640..d16196c7f 100644 --- a/tests/libs/util/program_helper.h +++ b/tests/libs/util/program_helper.h @@ -5,9 +5,9 @@ * @brief Utility functions for loading and attaching test eBPF programs. */ -#include "bpf.h" +#include "bpf/bpf.h" +#include "bpf/libbpf.h" #include "helpers.h" -#include "libbpf.h" typedef class _program_load_attach_helper { diff --git a/tests/performance/ExecutionContext.cpp b/tests/performance/ExecutionContext.cpp index 2d10ef424..70b4d57ab 100644 --- a/tests/performance/ExecutionContext.cpp +++ b/tests/performance/ExecutionContext.cpp @@ -169,8 +169,8 @@ static const char* _ebpf_map_type_t_to_string(ebpf_map_type_t type) { switch (type) { - case BPF_MAP_TYPE_UNSPECIFIED: - return "BPF_MAP_TYPE_UNSPECIFIED"; + case BPF_MAP_TYPE_UNSPEC: + return "BPF_MAP_TYPE_UNSPEC"; case BPF_MAP_TYPE_HASH: return "BPF_MAP_TYPE_HASH"; case BPF_MAP_TYPE_ARRAY: diff --git a/tests/performance/performance.h b/tests/performance/performance.h index 230f18f8e..94d33dbe3 100644 --- a/tests/performance/performance.h +++ b/tests/performance/performance.h @@ -10,7 +10,7 @@ #include "ebpf_object.h" #include "ebpf_program.h" #include "helpers.h" -#include "libbpf.h" +#include "bpf/libbpf.h" #include "performance_measure.h" #define PERF_TEST(FUNCTION) \ diff --git a/tests/sample/ext/app/sample_ext_app.cpp b/tests/sample/ext/app/sample_ext_app.cpp index b6bc9d058..792749b2e 100644 --- a/tests/sample/ext/app/sample_ext_app.cpp +++ b/tests/sample/ext/app/sample_ext_app.cpp @@ -7,10 +7,10 @@ #include #include -#include "bpf.h" +#include "bpf/bpf.h" +#include "bpf/libbpf.h" #include "catch_wrapper.hpp" #include "common_tests.h" -#include "libbpf.h" #include "netsh_test_helper.h" #include "program_helper.h" #include "service_helper.h" @@ -161,4 +161,4 @@ TEST_CASE("netsh_add_program_test_sample_ebpf", "[sample_ext_test]") _run_netsh_command(handle_ebpf_add_program, L"test_sample_ebpf.o", L"pinned=none", nullptr, &result); REQUIRE(result == NO_ERROR); REQUIRE(output.starts_with("Loaded with")); -} \ No newline at end of file +} diff --git a/tests/unit/libbpf_test.cpp b/tests/unit/libbpf_test.cpp index 61f3178e5..94ec46c89 100644 --- a/tests/unit/libbpf_test.cpp +++ b/tests/unit/libbpf_test.cpp @@ -3,13 +3,13 @@ #include #include -#include "bpf.h" -#include "catch_wrapper.hpp" -#include "helpers.h" +#include "bpf/bpf.h" #pragma warning(push) #pragma warning(disable : 4200) -#include "libbpf.h" +#include "bpf/libbpf.h" #pragma warning(pop) +#include "catch_wrapper.hpp" +#include "helpers.h" #include "platform.h" #include "program_helper.h" #include "test_helper.hpp" @@ -875,3 +875,49 @@ TEST_CASE("libbpf_prog_type_by_name", "[libbpf]") REQUIRE(prog_type == BPF_PROG_TYPE_XDP); REQUIRE(expected_attach_type == BPF_ATTACH_TYPE_XDP); } + +TEST_CASE("libbpf_get_error", "[libbpf]") +{ + errno = 123; + REQUIRE(libbpf_get_error(nullptr) == -123); +} + +TEST_CASE("bpf_object__load", "[libbpf]") +{ + _test_helper_libbpf test_helper; + + const char* my_object_name = "my_object_name"; + struct bpf_object_open_opts opts = {0}; + opts.object_name = my_object_name; + struct bpf_object* object = bpf_object__open_file("droppacket.o", &opts); + REQUIRE(object != nullptr); + + REQUIRE(strcmp(bpf_object__name(object), my_object_name) == 0); + + struct bpf_program* program = bpf_object__find_program_by_name(object, "DropPacket"); + REQUIRE(program != nullptr); + + REQUIRE(bpf_program__fd(program) == ebpf_fd_invalid); + REQUIRE(bpf_program__get_type(program) == BPF_PROG_TYPE_XDP); + + // Make sure we can override the program type if desired. + bpf_program__set_type(program, BPF_PROG_TYPE_BIND); + REQUIRE(bpf_program__get_type(program) == BPF_PROG_TYPE_BIND); + + bpf_program__set_type(program, BPF_PROG_TYPE_XDP); + + // Trying to attach the program should fail since it's not loaded yet. + bpf_link* link = bpf_program__attach(program); + REQUIRE(link == nullptr); + REQUIRE(libbpf_get_error(link) == -EINVAL); + + // Load the program. + REQUIRE(bpf_object__load(object) == 0); + + // Attach should now succeed. + link = bpf_program__attach(program); + REQUIRE(link != nullptr); + + REQUIRE(bpf_link__destroy(link) == 0); + bpf_object__close(object); +} diff --git a/tools/port_quota/port_quota.cpp b/tools/port_quota/port_quota.cpp index 3f410c0af..f9ebe37fd 100644 --- a/tools/port_quota/port_quota.cpp +++ b/tools/port_quota/port_quota.cpp @@ -5,9 +5,9 @@ #include #include #include -#include "bpf.h" +#include "bpf/bpf.h" +#include "bpf/libbpf.h" #include "ebpf_api.h" -#include "libbpf.h" const char* process_map = "port_quota::process_map"; const char* limits_map = "port_quota::limits_map";