Add netsh capability to unpin programs (#539)
* Add netsh capability to unpin programs * The netsh "set program id=<id> pinned=" (with no value) will now unpin a program from all paths * The netsh "delete program <id>" will now unpin a program from all paths before and releasing any reference held by netsh itself * Make the "attached=<string>" argument to netsh set programs work with a section name like string * Add libbpf api bpf_obj_get() * Add ebpf_get_next_pinned_program_name() API to enumerate pinned programs Fixes #190 #373 This is required for #188 which will update the "show programs" and also add an option to "add program" to pin all programs rather than just the first one in a file, like bpftool has such an option. Signed-off-by: Dave Thaler <dthaler@microsoft.com> * Update mock netsh behavior since PR 540 changed the underlying requirements PreprocessCommand now correctly matches tags so you can specify a later optional tag without having to specify earlier optional tags Signed-off-by: Dave Thaler <dthaler@microsoft.com>
This commit is contained in:
Родитель
6ae5c349dc
Коммит
872999db06
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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,9 +97,12 @@ 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)) {
|
||||
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) {
|
||||
if (section.find(prefix) == 0)
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,10 +334,14 @@ handle_ebpf_set_program(
|
|||
}
|
||||
case 1: // ATTACHED
|
||||
{
|
||||
if ((argv[current_index + i][0] != 0) &&
|
||||
(UuidFromStringW((RPC_WSTR)argv[current_index + i], &attach_type))) {
|
||||
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;
|
||||
}
|
||||
case 2: // PINNED
|
||||
|
@ -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";
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include <windows.h>
|
||||
#include <netsh.h> // Must be included after windows.h
|
||||
#include <string.h>
|
||||
#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);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright (c) Microsoft Corporation
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <io.h>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <windows.h>
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче