diff --git a/ebpfapi/Source.def b/ebpfapi/Source.def index 9f18dcdf1..78596a4b0 100644 --- a/ebpfapi/Source.def +++ b/ebpfapi/Source.def @@ -30,6 +30,7 @@ EXPORTS bpf_map_get_next_key bpf_map_lookup_elem bpf_map_update_elem + bpf_obj_get bpf_obj_get_info_by_fd bpf_obj_pin bpf_object__close @@ -72,6 +73,7 @@ EXPORTS ebpf_create_map_name ebpf_free_string ebpf_get_next_map + ebpf_get_next_pinned_program_name ebpf_get_next_program ebpf_get_program_type_by_name ebpf_link_close diff --git a/include/bpf.h b/include/bpf.h index 5d6802d2d..bf6432e27 100644 --- a/include/bpf.h +++ b/include/bpf.h @@ -183,6 +183,15 @@ 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. + * + * @return file descriptor for the pinned object, or -1 if not found. + */ +int +bpf_obj_get(const char* pathname); + /** * @brief Obtain information about the eBPF object referred to by bpf_fd. * This function populates up to info_len bytes of info, which will diff --git a/include/ebpf_api.h b/include/ebpf_api.h index 1480ce29d..ac745aef0 100644 --- a/include/ebpf_api.h +++ b/include/ebpf_api.h @@ -508,15 +508,6 @@ extern "C" ebpf_result_t ebpf_link_close(_In_ struct bpf_link* link); - /** - * @brief Get fd for a pinned object by pin path. - * @param[in] path Pin path for the object. - * - * @return file descriptor for the pinned object, -1 if not found. - */ - fd_t - ebpf_object_get(_In_z_ const char* path); - /** * @brief Close a file descriptor. Also close the underlying handle. * @param [in] fd File descriptor to be closed. @@ -543,6 +534,19 @@ extern "C" _Out_ ebpf_program_type_t* program_type, _Out_ ebpf_attach_type_t* expected_attach_type); + /** + * @brief Gets the next pinned program after a given name. + * + * @param[in] start_name Name to look for an entry greater than. + * @param[out] next_name Returns the next name, if one exists. + * + * @retval EBPF_SUCCESS The operation was successful. + * @retval EBPF_NO_MORE_KEYS No more entries found. + */ + ebpf_result_t + ebpf_get_next_pinned_program_name( + _In_z_ const char* start_name, _Out_writes_z_(EBPF_MAX_PIN_PATH_LENGTH) char* next_name); + #ifdef __cplusplus } #endif diff --git a/include/ebpf_structs.h b/include/ebpf_structs.h index c4c8d025d..e50b47b3c 100644 --- a/include/ebpf_structs.h +++ b/include/ebpf_structs.h @@ -120,3 +120,9 @@ struct bpf_prog_info // Windows-specific fields. ebpf_program_type_t type_uuid; ///< Program type UUID. }; + +typedef struct _ebpf_windows_program_type_data +{ + ebpf_program_type_t program_type_uuid; ///< Program type UUID. + ebpf_attach_type_t attach_type_uuid; ///< Attach type UUID. +} ebpf_windows_program_type_data_t; diff --git a/libs/api/api_internal.h b/libs/api/api_internal.h index 66b70cbd6..9894b17a5 100644 --- a/libs/api/api_internal.h +++ b/libs/api/api_internal.h @@ -368,3 +368,12 @@ ebpf_object_get_info_by_fd( */ ebpf_result_t ebpf_object_pin(fd_t fd, _In_z_ const char* path); + +/** + * @brief Get fd for a pinned object by pin path. + * @param[in] path Pin path for the object. + * + * @return file descriptor for the pinned object, -1 if not found. + */ +fd_t +ebpf_object_get(_In_z_ const char* path); diff --git a/libs/api/ebpf_api.cpp b/libs/api/ebpf_api.cpp index 4d1a6edcb..7e0c47f7c 100644 --- a/libs/api/ebpf_api.cpp +++ b/libs/api/ebpf_api.cpp @@ -1822,6 +1822,30 @@ ebpf_get_link_fd_by_id(ebpf_id_t id, _Out_ int* fd) noexcept return _get_fd_by_id(ebpf_operation_id_t::EBPF_OPERATION_GET_LINK_HANDLE_BY_ID, id, fd); } +ebpf_result_t +ebpf_get_next_pinned_program_name( + _In_z_ const char* start_name, _Out_writes_z_(EBPF_MAX_PIN_PATH_LENGTH) char* next_name) +{ + if (start_name == nullptr || next_name == nullptr) { + return EBPF_INVALID_ARGUMENT; + } + + _ebpf_operation_get_next_pinned_name_request request{ + sizeof(request), ebpf_operation_id_t::EBPF_OPERATION_GET_NEXT_PINNED_PROGRAM_NAME}; + _ebpf_operation_get_next_pinned_name_reply reply; + + strcpy_s(request.start_name, sizeof(request.start_name), start_name); + + uint32_t error = invoke_ioctl(request, reply); + ebpf_result_t result = windows_error_to_ebpf_result(error); + if (result != EBPF_SUCCESS) { + return result; + } + + strcpy_s(next_name, EBPF_MAX_PIN_PATH_LENGTH, reply.next_name); + return EBPF_SUCCESS; +} + static ebpf_result_t _get_next_id(ebpf_operation_id_t operation, ebpf_id_t start_id, _Out_ ebpf_id_t* next_id) { @@ -1896,10 +1920,10 @@ ebpf_get_program_type_by_name( } EbpfProgramType type = get_program_type_windows(name, name); - *program_type = *(GUID*)type.platform_specific_data; + ebpf_windows_program_type_data_t* data = (ebpf_windows_program_type_data_t*)type.platform_specific_data; - // TODO(issue #223): get expected attach type. - *expected_attach_type = EBPF_ATTACH_TYPE_UNSPECIFIED; + *program_type = data->program_type_uuid; + *expected_attach_type = data->attach_type_uuid; return EBPF_SUCCESS; } diff --git a/libs/api/libbpf_object.cpp b/libs/api/libbpf_object.cpp index ea1909a90..85e7f4c8d 100644 --- a/libs/api/libbpf_object.cpp +++ b/libs/api/libbpf_object.cpp @@ -77,3 +77,9 @@ bpf_obj_pin(int fd, const char* pathname) { return libbpf_result_err(ebpf_object_pin((fd_t)fd, pathname)); } + +int +bpf_obj_get(const char* pathname) +{ + return (int)ebpf_object_get(pathname); +} diff --git a/libs/api_common/windows_platform_common.cpp b/libs/api_common/windows_platform_common.cpp index 351eaddfa..e3744d853 100644 --- a/libs/api_common/windows_platform_common.cpp +++ b/libs/api_common/windows_platform_common.cpp @@ -35,14 +35,18 @@ const ebpf_context_descriptor_t g_xdp_context_descriptor = { EBPF_OFFSET_OF(xdp_md_t, data_end), EBPF_OFFSET_OF(xdp_md_t, data_meta)}; +const ebpf_windows_program_type_data_t windows_xdp_program_type_data = {EBPF_PROGRAM_TYPE_XDP, EBPF_ATTACH_TYPE_XDP}; + const EbpfProgramType windows_xdp_program_type = - PTYPE("xdp", &g_xdp_context_descriptor, (uint64_t)&EBPF_PROGRAM_TYPE_XDP, {"xdp"}); + PTYPE("xdp", &g_xdp_context_descriptor, (uint64_t)&windows_xdp_program_type_data, {"xdp"}); + +const ebpf_windows_program_type_data_t windows_bind_program_type_data = {EBPF_PROGRAM_TYPE_BIND, EBPF_ATTACH_TYPE_BIND}; const ebpf_context_descriptor_t g_bind_context_descriptor = { sizeof(bind_md_t), EBPF_OFFSET_OF(bind_md_t, app_id_start), EBPF_OFFSET_OF(bind_md_t, app_id_end), -1}; const EbpfProgramType windows_bind_program_type = - PTYPE("bind", &g_bind_context_descriptor, (uint64_t)&EBPF_PROGRAM_TYPE_BIND, {"bind"}); + PTYPE("bind", &g_bind_context_descriptor, (uint64_t)&windows_bind_program_type_data, {"bind"}); const ebpf_context_descriptor_t g_sample_ext_context_descriptor = { sizeof(sample_program_context_t), @@ -73,7 +77,8 @@ get_program_type_windows(const GUID& program_type) // info and then fill the EbpfProgramType struct. for (const EbpfProgramType t : windows_program_types) { if (t.platform_specific_data != 0) { - if (IsEqualGUID(*(GUID*)t.platform_specific_data, program_type)) { + ebpf_windows_program_type_data_t* data = (ebpf_windows_program_type_data_t*)t.platform_specific_data; + if (IsEqualGUID(data->program_type_uuid, program_type)) { return t; } } @@ -92,8 +97,11 @@ get_program_type_windows(const std::string& section, const std::string&) // prefixes and corresponding program and attach types. for (const EbpfProgramType t : windows_program_types) { if (program_type != nullptr) { - if (t.platform_specific_data != 0 && IsEqualGUID(*(GUID*)t.platform_specific_data, *program_type)) { - return t; + if (t.platform_specific_data != 0) { + ebpf_windows_program_type_data_t* data = (ebpf_windows_program_type_data_t*)t.platform_specific_data; + if (IsEqualGUID(data->program_type_uuid, *program_type)) { + return t; + } } } else { for (const std::string prefix : t.section_prefixes) { @@ -152,7 +160,9 @@ get_attach_type_windows(const std::string& section) for (const std::string prefix : t.section_prefixes) { if (section.find(prefix) == 0) { for (auto& [program_type, attach_type] : windows_program_type_to_attach_type) { - if (IsEqualGUID(*(GUID*)t.platform_specific_data, *program_type)) { + ebpf_windows_program_type_data_t* data = + (ebpf_windows_program_type_data_t*)t.platform_specific_data; + if (IsEqualGUID(data->attach_type_uuid, *attach_type)) { return attach_type; } } diff --git a/libs/ebpfnetsh/programs.cpp b/libs/ebpfnetsh/programs.cpp index da9fa04d3..f104dfc23 100644 --- a/libs/ebpfnetsh/programs.cpp +++ b/libs/ebpfnetsh/programs.cpp @@ -71,9 +71,7 @@ handle_ebpf_add_program( case 1: // TYPE { std::string type_name = down_cast_from_wstring(std::wstring(argv[current_index + i])); - ebpf_attach_type_t expected_attach_type; - ebpf_result_t result = - ebpf_get_program_type_by_name(type_name.c_str(), &program_type, &expected_attach_type); + ebpf_result_t result = ebpf_get_program_type_by_name(type_name.c_str(), &program_type, &attach_type); if (result != EBPF_SUCCESS) { status = ERROR_INVALID_PARAMETER; } @@ -117,6 +115,7 @@ handle_ebpf_add_program( return ERROR_SUPPRESS_OUTPUT; } + // TODO(#188): add option to pin all programs in the object. struct bpf_program* program = bpf_program__next(nullptr, object); struct bpf_link* link; result = ebpf_program_attach(program, (tags[1].bPresent ? &attach_type : nullptr), nullptr, 0, &link); @@ -146,6 +145,40 @@ handle_ebpf_add_program( return ERROR_SUCCESS; } +static DWORD +_unpin_program_by_id(ebpf_id_t id) +{ + ebpf_result_t result; + DWORD status = NO_ERROR; + char name[EBPF_MAX_PIN_PATH_LENGTH] = {0}; + + for (;;) { + result = ebpf_get_next_pinned_program_name(name, name); + if (result != EBPF_SUCCESS) { + break; + } + int fd = bpf_obj_get(name); + if (fd < 0) { + continue; + } + bpf_prog_info info; + uint32_t info_size = sizeof(info); + if (bpf_obj_get_info_by_fd(fd, &info, &info_size) == 0) { + if (id == info.id) { + result = ebpf_object_unpin(name); + if (result != EBPF_SUCCESS) { + printf("Error %d unpinning %d from %s\n", result, id, name); + status = ERROR_SUPPRESS_OUTPUT; + } else { + printf("Unpinned %d from %s\n", id, name); + } + } + } + Platform::_close(fd); + } + return status; +} + DWORD handle_ebpf_delete_program( LPCWSTR machine, LPWSTR* argv, DWORD current_index, DWORD argc, DWORD flags, LPCVOID data, BOOL* done) @@ -180,9 +213,11 @@ handle_ebpf_delete_program( return status; } - // TODO(issue #190): If the program is pinned, unpin the specified program. - // The temporary API ebpf_api_unpin_object requires knowing the name a priori - // and we have no way to get it yet. + // If the program is pinned, unpin the specified program. + status = _unpin_program_by_id(id); + if (status != NO_ERROR) { + return status; + } // Remove from our list of programs to release our own reference if we took one. // If there are no other references to the program, it will be unloaded. @@ -205,7 +240,7 @@ handle_ebpf_delete_program( // TODO: see if the program is still loaded, in which case some other process holds // a reference. Get the PID of that process and display it. - return ERROR_OKAY; + return NO_ERROR; } } } @@ -299,9 +334,13 @@ handle_ebpf_set_program( } case 1: // ATTACHED { - if ((argv[current_index + i][0] != 0) && - (UuidFromStringW((RPC_WSTR)argv[current_index + i], &attach_type))) { - status = ERROR_INVALID_SYNTAX; + if (argv[current_index + i][0] != 0) { + std::string type_name = down_cast_from_wstring(std::wstring(argv[current_index + i])); + ebpf_program_type_t program_type; + ebpf_result_t result = ebpf_get_program_type_by_name(type_name.c_str(), &program_type, &attach_type); + if (result != EBPF_SUCCESS) { + status = ERROR_INVALID_SYNTAX; + } } break; } @@ -321,7 +360,7 @@ handle_ebpf_set_program( if (memcmp(&attach_type, &EBPF_ATTACH_TYPE_UNSPECIFIED, sizeof(ebpf_attach_type_t)) != 0) { ebpf_result_t result = ebpf_program_attach_by_id(id, attach_type); if (result != NO_ERROR) { - std::cerr << "error " << result << ": could not detach program" << std::endl; + std::cerr << "error " << result << ": could not attach program" << std::endl; return ERROR_SUPPRESS_OUTPUT; } } else { @@ -335,10 +374,8 @@ handle_ebpf_set_program( if (tags[2].bPresent) { if (pinned.empty()) { - // TODO (issue #190): call ebpf_program_unpin() once it exists. - // The temporary API ebpf_api_unpin_object requires knowing the name a priori - // and we have no way to get it. - return ERROR_CALL_NOT_IMPLEMENTED; + // Unpin a program from all names to which it is currently pinned. + return _unpin_program_by_id(id); } else { // Try to find the program with the specified ID. fd_t program_fd = bpf_prog_get_fd_by_id(id); @@ -460,7 +497,7 @@ handle_ebpf_show_programs( level = VL_VERBOSE; } - // TODO(#190): We need to implement level, other columns, and implement filtering by attached and pinned. + // TODO(#188): We need to implement level, other columns, and implement filtering by attached and pinned. std::cout << "\n"; std::cout << " ID File Name Section Name Mode\n"; diff --git a/libs/execution_context/ebpf_core.c b/libs/execution_context/ebpf_core.c index a549aa846..a55024ace 100644 --- a/libs/execution_context/ebpf_core.c +++ b/libs/execution_context/ebpf_core.c @@ -1069,6 +1069,18 @@ _ebpf_core_protocol_get_next_program_id( return _get_next_id(EBPF_OBJECT_PROGRAM, request, reply, reply_length); } +static ebpf_result_t +_ebpf_core_protocol_get_next_pinned_program_name( + _In_ const ebpf_operation_get_next_pinned_name_request_t* request, + _Out_ ebpf_operation_get_next_pinned_name_reply_t* reply, + uint16_t reply_length) +{ + UNREFERENCED_PARAMETER(reply_length); + + return ebpf_pinning_table_get_next_name( + _ebpf_core_map_pinning_table, EBPF_OBJECT_PROGRAM, request->start_name, reply->next_name); +} + static ebpf_result_t _ebpf_core_protocol_get_object_info( _In_ const ebpf_operation_get_object_info_request_t* request, @@ -1323,6 +1335,11 @@ static ebpf_protocol_handler_t _ebpf_protocol_handlers[] = { {(ebpf_result_t(__cdecl*)(const void*))_ebpf_core_protocol_get_object_info, sizeof(ebpf_operation_get_object_info_request_t), sizeof(ebpf_operation_get_object_info_reply_t)}, + + // EBPF_OPERATION_GET_NEXT_PINNED_PROGRAM_NAME + {(ebpf_result_t(__cdecl*)(const void*))_ebpf_core_protocol_get_next_pinned_program_name, + sizeof(ebpf_operation_get_next_pinned_name_request_t), + sizeof(ebpf_operation_get_next_pinned_name_reply_t)}, }; ebpf_result_t diff --git a/libs/execution_context/ebpf_protocol.h b/libs/execution_context/ebpf_protocol.h index e670ad789..dae6052c3 100644 --- a/libs/execution_context/ebpf_protocol.h +++ b/libs/execution_context/ebpf_protocol.h @@ -38,6 +38,7 @@ typedef enum _ebpf_operation_id EBPF_OPERATION_GET_NEXT_MAP_ID, EBPF_OPERATION_GET_NEXT_PROGRAM_ID, EBPF_OPERATION_GET_OBJECT_INFO, + EBPF_OPERATION_GET_NEXT_PINNED_PROGRAM_NAME, } ebpf_operation_id_t; typedef enum _ebpf_code_type @@ -333,6 +334,18 @@ typedef struct _ebpf_operation_get_next_id_reply ebpf_id_t next_id; } ebpf_operation_get_next_id_reply_t; +typedef struct _ebpf_operation_get_next_pinned_name_request +{ + struct _ebpf_operation_header header; + char start_name[EBPF_MAX_PIN_PATH_LENGTH]; +} ebpf_operation_get_next_pinned_name_request_t; + +typedef struct _ebpf_operation_get_next_pinned_name_reply +{ + struct _ebpf_operation_header header; + char next_name[EBPF_MAX_PIN_PATH_LENGTH]; +} ebpf_operation_get_next_pinned_name_reply_t; + typedef struct _ebpf_operation_get_object_info_request { struct _ebpf_operation_header header; diff --git a/libs/platform/ebpf_pinning_table.c b/libs/platform/ebpf_pinning_table.c index 92f475c70..ade37b8e8 100644 --- a/libs/platform/ebpf_pinning_table.c +++ b/libs/platform/ebpf_pinning_table.c @@ -14,9 +14,9 @@ // key. Delete erases the entry from the ebpf_hash_table_t, but doesn't free the memory associated with the // ebpf_pinning_entry_t. -#include "ebpf_pinning_table.h" - +#include "ebpf_core_structs.h" #include "ebpf_object.h" +#include "ebpf_pinning_table.h" #define EBPF_PINNING_TABLE_BUCKET_COUNT 64 @@ -109,6 +109,10 @@ ebpf_pinning_table_insert(ebpf_pinning_table_t* pinning_table, const ebpf_utf8_s ebpf_utf8_string_t* new_key; ebpf_pinning_entry_t* new_pinning_entry; + if (name->length >= EBPF_MAX_PIN_PATH_LENGTH) { + return EBPF_INVALID_ARGUMENT; + } + new_pinning_entry = ebpf_allocate(sizeof(ebpf_pinning_entry_t)); if (!new_pinning_entry) { return_value = EBPF_NO_MEMORY; @@ -285,6 +289,52 @@ Exit: return result; } +ebpf_result_t +ebpf_pinning_table_get_next_name( + _In_ ebpf_pinning_table_t* pinning_table, + ebpf_object_type_t object_type, + _In_z_ const char* start_name, + _Out_writes_z_(EBPF_MAX_PIN_PATH_LENGTH) char* next_name) +{ + if ((pinning_table == NULL) || (start_name == NULL) || (next_name == NULL)) { + return EBPF_INVALID_ARGUMENT; + } + + // Copy the name provided into a buffer that we can guarantee is null terminated. + char name_buffer[EBPF_MAX_PIN_PATH_LENGTH]; + strncpy_s(name_buffer, EBPF_MAX_PIN_PATH_LENGTH, start_name, _TRUNCATE); + const ebpf_utf8_string_t name_string = {(uint8_t*)name_buffer, strlen(name_buffer)}; + const ebpf_utf8_string_t* name = &name_string; + const uint8_t* previous_key = (name->length == 0) ? NULL : (const uint8_t*)&name; + + ebpf_lock_state_t state = ebpf_lock_lock(&pinning_table->lock); + + ebpf_result_t result; + ebpf_pinning_entry_t** next_pinning_entry = NULL; + + for (;;) { + // Get the next entry in the table. + ebpf_utf8_string_t* next_object_name; + result = ebpf_hash_table_next_key_and_value( + pinning_table->hash_table, previous_key, (uint8_t*)&next_object_name, (uint8_t**)&next_pinning_entry); + if (result != EBPF_SUCCESS) { + break; + } + + // See if the entry matches the object type the caller is interested in. + if (object_type == ebpf_object_get_type((*next_pinning_entry)->object)) { + // Copy the name into the caller's buffer. + strncpy_s( + next_name, EBPF_MAX_PIN_PATH_LENGTH, (const char*)next_object_name->value, next_object_name->length); + break; + } + previous_key = (uint8_t*)&next_object_name; + } + + ebpf_lock_unlock(&pinning_table->lock, state); + return result; +} + void ebpf_pinning_entries_release(uint16_t entry_count, _In_opt_count_(entry_count) ebpf_pinning_entry_t* pinning_entries) { diff --git a/libs/platform/ebpf_pinning_table.h b/libs/platform/ebpf_pinning_table.h index ba70ee8d1..0c302fb02 100644 --- a/libs/platform/ebpf_pinning_table.h +++ b/libs/platform/ebpf_pinning_table.h @@ -101,6 +101,23 @@ extern "C" _Out_ uint16_t* entry_count, _Outptr_result_buffer_maybenull_(*entry_count) ebpf_pinning_entry_t** pinning_entries); + /** + * @brief Gets the next name in the pinning table after a given name. + * + * @param[in] pinning_table Pinning table to enumerate. + * @param[in] object_type Object type. + * @param[in] start_name Name to look for an entry greater than. + * @param[out] next_name Returns the next name, if one exists. + * @retval EBPF_SUCCESS The operation was successful. + * @retval EBPF_NO_MORE_KEYS No more entries found. + */ + ebpf_result_t + ebpf_pinning_table_get_next_name( + _In_ ebpf_pinning_table_t* pinning_table, + ebpf_object_type_t object_type, + _In_z_ const char* start_name, + _Out_writes_z_(EBPF_MAX_PIN_PATH_LENGTH) char* next_name); + /** * @brief Releases entries returned by ebpf_pinning_table_enumerate_entries. * @param[in] entry_count Length of input array of entries. diff --git a/tests/end_to_end/end_to_end.cpp b/tests/end_to_end/end_to_end.cpp index 7041606ef..6b64ba26f 100644 --- a/tests/end_to_end/end_to_end.cpp +++ b/tests/end_to_end/end_to_end.cpp @@ -579,18 +579,22 @@ TEST_CASE("map_pinning_test", "[end_to_end]") bpf_map__pin(bpf_object__find_map_by_name(object, "process_map"), process_maps_name.c_str()) == EBPF_SUCCESS); REQUIRE(bpf_map__pin(bpf_object__find_map_by_name(object, "limits_map"), limit_maps_name.c_str()) == EBPF_SUCCESS); - REQUIRE(ebpf_object_get(process_maps_name.c_str()) != ebpf_fd_invalid); + fd_t fd = bpf_obj_get(process_maps_name.c_str()); + REQUIRE(fd != ebpf_fd_invalid); + Platform::_close(fd); - REQUIRE(ebpf_object_get(limit_maps_name.c_str()) != ebpf_fd_invalid); + fd = bpf_obj_get(limit_maps_name.c_str()); + REQUIRE(fd != ebpf_fd_invalid); + Platform::_close(fd); REQUIRE( bpf_map__unpin(bpf_object__find_map_by_name(object, "process_map"), process_maps_name.c_str()) == EBPF_SUCCESS); REQUIRE( bpf_map__unpin(bpf_object__find_map_by_name(object, "limits_map"), limit_maps_name.c_str()) == EBPF_SUCCESS); - REQUIRE(ebpf_object_get(limit_maps_name.c_str()) == ebpf_fd_invalid); + REQUIRE(bpf_obj_get(limit_maps_name.c_str()) == ebpf_fd_invalid); - REQUIRE(ebpf_object_get(process_maps_name.c_str()) == ebpf_fd_invalid); + REQUIRE(bpf_obj_get(process_maps_name.c_str()) == ebpf_fd_invalid); bpf_object__close(object); } diff --git a/tests/end_to_end/netsh_test.cpp b/tests/end_to_end/netsh_test.cpp index 0c8d8afb0..23e76c493 100644 --- a/tests/end_to_end/netsh_test.cpp +++ b/tests/end_to_end/netsh_test.cpp @@ -3,6 +3,7 @@ #include #include // Must be included after windows.h +#include #include "bpf.h" #include "capture_helper.hpp" #include "catch_wrapper.hpp" @@ -32,7 +33,6 @@ PreprocessCommand( _Out_writes_opt_(dwArgCount - dwCurrentIndex) DWORD* pdwTagType) { UNREFERENCED_PARAMETER(hModule); - UNREFERENCED_PARAMETER(ppwcArguments); DWORD argc = dwArgCount - dwCurrentIndex; if (argc < dwMinArgs || argc > dwMaxArgs) { @@ -43,12 +43,42 @@ PreprocessCommand( return ERROR_INVALID_SYNTAX; } - // Simplified algorithm is to assume arguments are supplied in the correct order. + for (DWORD i = 0; i < argc; i++) { + PWSTR equals = wcschr(ppwcArguments[dwCurrentIndex + i], L'='); + PWSTR tagName = nullptr; + if (equals) { + tagName = _wcsdup(ppwcArguments[dwCurrentIndex + i]); + if (tagName == nullptr) { + return ERROR_OUTOFMEMORY; + } + tagName[equals - ppwcArguments[dwCurrentIndex + i]] = 0; + + // Advance past the tag. + ppwcArguments[dwCurrentIndex + i] = ++equals; + } + + // Find which tag this argument goes with. + DWORD dwTagIndex; + for (dwTagIndex = 0; dwTagIndex < dwTagCount; dwTagIndex++) { + if ((tagName == nullptr && !pttTags[dwTagIndex].bPresent) || + (tagName != nullptr && wcsncmp(pttTags[dwTagIndex].pwszTag, tagName, wcslen(tagName)) == 0)) { + pttTags[dwTagIndex].bPresent = true; + pdwTagType[i] = dwTagIndex; + break; + } + } + if (tagName) { + free((void*)tagName); + } + if (dwTagIndex == dwTagCount) { + // Tag not found. + return ERROR_INVALID_SYNTAX; + } + } + + // See if any required tags are absent. for (DWORD i = 0; i < dwTagCount; i++) { - if (dwCurrentIndex + i < dwArgCount) { - pttTags[i].bPresent = true; - pdwTagType[i] = i; - } else if (pttTags[i].dwRequired & NS_REQ_PRESENT) { + if (!pttTags[i].bPresent && (pttTags[i].dwRequired & NS_REQ_PRESENT)) { return ERROR_INVALID_SYNTAX; } } @@ -275,6 +305,7 @@ TEST_CASE("set program", "[netsh][programs]") int result; std::string output = _run_netsh_command(handle_ebpf_add_program, L"tail_call.o", nullptr, nullptr, &result); REQUIRE(strcmp(output.c_str(), "Loaded with ID 196609\n") == 0); + REQUIRE(result == NO_ERROR); // Detach the program. This won't delete the program since // the containing object is still associated with the netsh process, @@ -306,7 +337,7 @@ TEST_CASE("set program", "[netsh][programs]") RpcStringFreeW(&attach_type_string); output = _run_netsh_command(handle_ebpf_delete_program, L"196609", nullptr, nullptr, &result); REQUIRE(output == ""); - REQUIRE(result == ERROR_OKAY); + REQUIRE(result == NO_ERROR); REQUIRE(bpf_object__next(nullptr) == nullptr); // Verify the program ID doesn't exist any more. @@ -315,7 +346,6 @@ TEST_CASE("set program", "[netsh][programs]") output == "\n" " ID File Name Section Name Mode\n" "====== ==================== =============== ================ =========\n"); - REQUIRE(result == NO_ERROR); } @@ -345,3 +375,63 @@ TEST_CASE("show maps", "[netsh][maps]") Platform::_close(inner_map_fd); Platform::_close(outer_map_fd); } + +TEST_CASE("delete pinned program", "[netsh][programs]") +{ + _test_helper_libbpf test_helper; + + // Load a program unpinned. + int result; + std::string output = _run_netsh_command(handle_ebpf_add_program, L"tail_call.o", nullptr, nullptr, &result); + REQUIRE(strcmp(output.c_str(), "Loaded with ID 196609\n") == 0); + REQUIRE(result == NO_ERROR); + + // Pin the program. + output = _run_netsh_command(handle_ebpf_set_program, L"196609", L"pinned=mypinname", nullptr, &result); + REQUIRE(result == ERROR_OKAY); + REQUIRE(output == ""); + + // Verify we can delete a pinned program. + output = _run_netsh_command(handle_ebpf_delete_program, L"196609", nullptr, nullptr, &result); + REQUIRE(output == "Unpinned 196609 from mypinname\n"); + REQUIRE(result == NO_ERROR); + REQUIRE(bpf_object__next(nullptr) == nullptr); + + // Verify the program ID doesn't exist any more. + output = _run_netsh_command(handle_ebpf_show_programs, nullptr, nullptr, nullptr, &result); + REQUIRE( + output == "\n" + " ID File Name Section Name Mode\n" + "====== ==================== =============== ================ =========\n"); + REQUIRE(result == NO_ERROR); +} + +TEST_CASE("unpin program", "[netsh][programs]") +{ + _test_helper_libbpf test_helper; + + // Load a program pinned. + int result; + std::string output = _run_netsh_command(handle_ebpf_add_program, L"tail_call.o", L"xdp", L"mypinname", &result); + REQUIRE(strcmp(output.c_str(), "Loaded with ID 196609\n") == 0); + REQUIRE(result == NO_ERROR); + + // Unpin the program. + output = _run_netsh_command(handle_ebpf_set_program, L"196609", L"", nullptr, &result); + REQUIRE(result == ERROR_OKAY); + REQUIRE(output == ""); + + // Verify we can delete the unpinned program. + output = _run_netsh_command(handle_ebpf_delete_program, L"196609", nullptr, nullptr, &result); + REQUIRE(output == "Unpinned 196609 from mypinname\n"); + REQUIRE(result == NO_ERROR); + REQUIRE(bpf_object__next(nullptr) == nullptr); + + // Verify the program ID doesn't exist any more. + output = _run_netsh_command(handle_ebpf_show_programs, nullptr, nullptr, nullptr, &result); + REQUIRE( + output == "\n" + " ID File Name Section Name Mode\n" + "====== ==================== =============== ================ =========\n"); + REQUIRE(result == NO_ERROR); +} diff --git a/tools/port_quota/port_quota.cpp b/tools/port_quota/port_quota.cpp index b62f44a80..3f410c0af 100644 --- a/tools/port_quota/port_quota.cpp +++ b/tools/port_quota/port_quota.cpp @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation // SPDX-License-Identifier: MIT +#include #include #include #include @@ -106,7 +107,7 @@ stats(int argc, char** argv) UNREFERENCED_PARAMETER(argc); UNREFERENCED_PARAMETER(argv); - map_fd = ebpf_object_get((char*)process_map); + map_fd = bpf_obj_get((char*)process_map); if (map_fd == ebpf_fd_invalid) { fprintf(stderr, "Failed to look up eBPF map\n"); return 1; @@ -124,6 +125,7 @@ stats(int argc, char** argv) printf("%lld\t%d\t%S\n", pid, process_entry.count, process_entry.name); result = bpf_map_get_next_key(map_fd, &pid, &pid); }; + _close(map_fd); return 0; } @@ -141,7 +143,7 @@ limit(int argc, char** argv) uint32_t result; uint32_t key = 0; - map_fd = ebpf_object_get((char*)limits_map); + map_fd = bpf_obj_get((char*)limits_map); if (map_fd == ebpf_fd_invalid) { fprintf(stderr, "Failed to look up eBPF map.\n"); return 1; @@ -153,6 +155,8 @@ limit(int argc, char** argv) return 1; } + _close(map_fd); + return 0; }