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:
Dave Thaler 2021-09-15 18:40:44 -07:00 коммит произвёл GitHub
Родитель 6ae5c349dc
Коммит 872999db06
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
16 изменённых файлов: 352 добавлений и 50 удалений

Просмотреть файл

@ -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,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;
}
}

Просмотреть файл

@ -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";

Просмотреть файл

@ -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;
}