Skip to content

Commit

Permalink
Add ability to enumerate maps associated with a program (microsoft#1395)
Browse files Browse the repository at this point in the history
* Add map ids test

Signed-off-by: Dave Thaler <[email protected]>

* Enumerate maps associated with a program

Fixes microsoft#1339

Signed-off-by: Dave Thaler <[email protected]>

* Initialize info before calling

Signed-off-by: Dave Thaler <[email protected]>

* Update bpftool

Signed-off-by: Dave Thaler <[email protected]>

* Make "netsh show prog l=v" show map ids for program

Signed-off-by: Dave Thaler <[email protected]>

* Return EFAULT if map_ids is not a valid pointer

Signed-off-by: Dave Thaler <[email protected]>

* Fix warning

Signed-off-by: Dave Thaler <[email protected]>

* More test fixes

Signed-off-by: Dave Thaler <[email protected]>

* Fix test

Signed-off-by: Dave Thaler <[email protected]>

* Fix socket test

Signed-off-by: Dave Thaler <[email protected]>

* Add more checks in the socket test

Signed-off-by: Dave Thaler <[email protected]>

* Fix socket test

Signed-off-by: Dave Thaler <[email protected]>

* Fix netsh

Also fix GettingStarted.md and a missing space in the logs
that were found while testing this fix.

Signed-off-by: Dave Thaler <[email protected]>

* Set map_ids on output

Signed-off-by: Dave Thaler <[email protected]>

Signed-off-by: Dave Thaler <[email protected]>
  • Loading branch information
dthaler authored Sep 30, 2022
1 parent 3b71c61 commit 568e8f0
Show file tree
Hide file tree
Showing 27 changed files with 210 additions and 67 deletions.
6 changes: 3 additions & 3 deletions docs/GettingStarted.md
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ This tests the XDP_TX functionality.
2. Load the test eBPF program by running the following command: `netsh ebpf add program reflect_packet.o xdp` and note the ID. See **Note 3** below.
2. On the second host:
1. Allow inbound traffic for `xdp_tests.exe` through Windows Defender Firewall. See **Note 1** below.
2. Run `xdp_tests.exe xdp_reflect_test --remote_ip <IP on the first host>`. See **Note 2** below.
2. Run `xdp_tests.exe xdp_reflect_test --remote-ip <IP on the first host>`. See **Note 2** below.

#### Encapsulation Test
This uses `bpf_xdp_adjust_head` helper function to encapsulate an outer IP header to a packet.
Expand All @@ -270,7 +270,7 @@ This uses `bpf_xdp_adjust_head` helper function to encapsulate an outer IP heade
2. Load the test eBPF program by running the following command: `netsh ebpf add program encap_reflect_packet.o xdp` and note the ID. See **Note 3** below.
2. On the second host:
1. Allow inbound traffic for `xdp_tests.exe` through Windows Defender Firewall. See **Note 1** below.
2. Run `xdp_tests.exe xdp_encap_reflect_test --remote_ip <IP on the first host>`. See **Note 2** below.
2. Run `xdp_tests.exe xdp_encap_reflect_test --remote-ip <IP on the first host>`. See **Note 2** below.

#### Decapsulation Test
This uses `bpf_xdp_adjust_head` helper function to decapsulate an outer IP header from a packet.
Expand All @@ -279,7 +279,7 @@ This uses `bpf_xdp_adjust_head` helper function to decapsulate an outer IP heade
3. On the second host:
1. Load the second test eBPF program by running the following command: `netsh ebpf add program decap_permit_packet.o xdp` and note the ID. See **Note 3** below.
2. Allow inbound traffic for `xdp_tests.exe` through Windows Defender Firewall. See **Note 1** below.
3. Run `xdp_tests.exe xdp_reflect_test --remote_ip <IP on the first host>`. See **Note 2** below.
3. Run `xdp_tests.exe xdp_reflect_test --remote-ip <IP on the first host>`. See **Note 2** below.

**Note 1:** To allow inbound traffic to `xdp_tests.exe`, in a Windows Powershell with administrative privilege, run `New-NetFirewallRule -DisplayName "XDP_Test" -Program "<Full path to xdp_tests.exe>" -Direction Inbound -Action Allow`.<br>
**Note 2:** For the `--remote-ip` parameter to `xdp_tests.exe` program that is run on the second host, pass an IPv4 or IPv6 address of an Ethernet-like interface on the first host in string format.<br>
Expand Down
2 changes: 1 addition & 1 deletion external/bpftool
Submodule bpftool updated 2 files
+1 −2 src/main.h
+4 −9 src/prog.c
1 change: 1 addition & 0 deletions include/bpf/bpf.h
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ bpf_obj_get(const char* pathname);
* write into the info. On output, contains the actual number of bytes written.
*
* @retval 0 The operation was successful.
* @retval -EFAULT A pointer passed in the input info was invalid.
* @retval <0 An error occured, and errno was set.
*/
int
Expand Down
5 changes: 4 additions & 1 deletion include/ebpf_result.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,12 @@ extern "C"

/// Operation was canceled.
EBPF_CANCELED, // = 30

/// Invalid pointer.
EBPF_INVALID_POINTER,
} ebpf_result_t;

#define EBPF_RESULT_COUNT (EBPF_CANCELED + 1)
#define EBPF_RESULT_COUNT (EBPF_INVALID_POINTER + 1)

#ifdef __cplusplus
}
Expand Down
1 change: 1 addition & 0 deletions include/ebpf_structs.h
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,7 @@ struct bpf_prog_info
ebpf_id_t id; ///< Program ID.
enum bpf_prog_type type; ///< Program type, if a cross-platform type.
uint32_t nr_map_ids; ///< Number of maps associated with this program.
uintptr_t map_ids; ///< Pointer to caller-allocated array to fill map IDs into.
char name[BPF_OBJ_NAME_LEN]; ///< Null-terminated program name.

// Windows-specific fields.
Expand Down
4 changes: 4 additions & 0 deletions include/ebpf_utilities.h
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,10 @@ win32_error_code_to_ebpf_result(uint32_t error)
result = EBPF_CANCELED;
break;

case ERROR_NOACCESS:
result = EBPF_INVALID_POINTER;
break;

default:
result = EBPF_FAILED;
break;
Expand Down
5 changes: 3 additions & 2 deletions libs/api/api_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -412,7 +412,8 @@ ebpf_get_next_program_id(ebpf_id_t start_id, ebpf_id_t _Out_* next_id) noexcept;
* * struct bpf_prog_info
*
* @param[in] bpf_fd File descriptor referring to an eBPF object.
* @param[out] info Pointer to memory in which to write the info obtained.
* @param[in,out] info Pointer to memory in which to write the info obtained.
* On input, contains any additional parameters to use.
* @param[in,out] info_size On input, contains the maximum number of bytes to
* write into the info. On output, contains the actual number of bytes written.
*
Expand All @@ -421,7 +422,7 @@ ebpf_get_next_program_id(ebpf_id_t start_id, ebpf_id_t _Out_* next_id) noexcept;
*/
ebpf_result_t
ebpf_object_get_info_by_fd(
fd_t bpf_fd, _Out_writes_bytes_to_(*info_size, *info_size) void* info, _Inout_ uint32_t* info_size) noexcept;
fd_t bpf_fd, _Inout_updates_bytes_to_(*info_size, *info_size) void* info, _Inout_ uint32_t* info_size) noexcept;

/**
* @brief Pin an object to the specified path.
Expand Down
10 changes: 5 additions & 5 deletions libs/api/ebpf_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1455,7 +1455,7 @@ initialize_map(_Out_ ebpf_map_t* map, _In_ const map_cache_t& map_cache) noexcep
// Set the inner map ID if we have a real inner map fd.
map->map_definition.inner_map_id = EBPF_ID_NONE;
if (map_cache.verifier_map_descriptor.inner_map_fd != ebpf_fd_invalid) {
struct bpf_map_info info;
struct bpf_map_info info = {0};
uint32_t info_size = (uint32_t)sizeof(info);
if (ebpf_object_get_info_by_fd(map_cache.verifier_map_descriptor.inner_map_fd, &info, &info_size) ==
EBPF_SUCCESS) {
Expand Down Expand Up @@ -1486,7 +1486,7 @@ _initialize_ebpf_maps_native(
result = EBPF_INVALID_ARGUMENT;
goto Exit;
}
struct bpf_map_info info;
struct bpf_map_info info = {0};
uint32_t info_size = (uint32_t)sizeof(info);
result = ebpf_object_get_info(map_handles[i], &info, &info_size);
if (result != EBPF_SUCCESS) {
Expand Down Expand Up @@ -1539,7 +1539,7 @@ _initialize_ebpf_programs_native(
result = EBPF_INVALID_ARGUMENT;
goto Exit;
}
struct bpf_prog_info info;
struct bpf_prog_info info = {};
uint32_t info_size = (uint32_t)sizeof(info);
result = ebpf_object_get_info(program_handles[i], &info, &info_size);
if (result != EBPF_SUCCESS) {
Expand Down Expand Up @@ -2238,7 +2238,7 @@ _ebpf_validate_map(_In_ ebpf_map_t* map, fd_t original_map_fd) noexcept
EBPF_LOG_ENTRY();
ebpf_assert(map);
// Validate that the existing map definition matches with this new map.
struct bpf_map_info info;
struct bpf_map_info info = {0};
fd_t inner_map_info_fd = ebpf_fd_invalid;
uint32_t info_size = (uint32_t)sizeof(info);
ebpf_result_t result = ebpf_object_get_info_by_fd(original_map_fd, &info, &info_size);
Expand Down Expand Up @@ -3237,7 +3237,7 @@ ebpf_get_next_program_id(ebpf_id_t start_id, _Out_ ebpf_id_t* next_id) noexcept

ebpf_result_t
ebpf_object_get_info_by_fd(
fd_t bpf_fd, _Out_writes_bytes_to_(*info_size, *info_size) void* info, _Inout_ uint32_t* info_size) noexcept
fd_t bpf_fd, _Inout_updates_bytes_to_(*info_size, *info_size) void* info, _Inout_ uint32_t* info_size) noexcept
{
EBPF_LOG_ENTRY();
ebpf_assert(info);
Expand Down
7 changes: 4 additions & 3 deletions libs/api_common/api_common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ get_file_size(const char* filename, size_t* byte_code_size) noexcept
ebpf_result_t
ebpf_object_get_info(
ebpf_handle_t handle,
_Out_writes_bytes_to_(*info_size, *info_size) void* info,
_Inout_updates_bytes_to_(*info_size, *info_size) void* info,
_Inout_ uint32_t* info_size) noexcept
{
EBPF_LOG_ENTRY();
Expand All @@ -74,14 +74,15 @@ ebpf_object_get_info(
return EBPF_INVALID_FD;
}

ebpf_protocol_buffer_t request_buffer(sizeof(ebpf_operation_get_object_info_request_t));
ebpf_protocol_buffer_t request_buffer(sizeof(ebpf_operation_get_object_info_request_t) + *info_size);
ebpf_protocol_buffer_t reply_buffer(EBPF_OFFSET_OF(ebpf_operation_get_object_info_reply_t, info) + *info_size);
auto request = reinterpret_cast<ebpf_operation_get_object_info_request_t*>(request_buffer.data());
auto reply = reinterpret_cast<ebpf_operation_get_object_info_reply_t*>(reply_buffer.data());

request->header.length = static_cast<uint16_t>(request_buffer.size());
request->header.id = ebpf_operation_id_t::EBPF_OPERATION_GET_OBJECT_INFO;
request->handle = handle;
memcpy(request->info, info, *info_size);

ebpf_result_t result = win32_error_code_to_ebpf_result(invoke_ioctl(request_buffer, reply_buffer));
if (result == EBPF_SUCCESS) {
Expand All @@ -101,7 +102,7 @@ query_map_definition(
_Out_ uint32_t* max_entries,
_Out_ ebpf_id_t* inner_map_id) noexcept
{
bpf_map_info info;
bpf_map_info info = {0};
uint32_t info_size = sizeof(info);
ebpf_result_t result = ebpf_object_get_info(handle, &info, &info_size);
if (result == EBPF_SUCCESS) {
Expand Down
6 changes: 5 additions & 1 deletion libs/api_common/api_common.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,10 @@ ebpf_result_to_errno(ebpf_result_t result)
error = EACCES;
break;

case EBPF_INVALID_POINTER:
error = EFAULT;
break;

default:
error = EOTHER;
break;
Expand All @@ -139,7 +143,7 @@ ebpf_result_to_errno(ebpf_result_t result)
ebpf_result_t
ebpf_object_get_info(
ebpf_handle_t handle,
_Out_writes_bytes_to_(*info_size, *info_size) void* info,
_Inout_updates_bytes_to_(*info_size, *info_size) void* info,
_Inout_ uint32_t* info_size) noexcept;

ebpf_result_t
Expand Down
2 changes: 1 addition & 1 deletion libs/ebpfnetsh/pins.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ handle_ebpf_show_pins(
continue;
}

struct bpf_prog_info info;
struct bpf_prog_info info = {};
uint32_t info_size = (uint32_t)sizeof(info);
if (bpf_obj_get_info_by_fd(program_fd, &info, &info_size) == 0) {
printf("%7u Program %s\n", info.id, path.c_str());
Expand Down
29 changes: 24 additions & 5 deletions libs/ebpfnetsh/programs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ handle_ebpf_add_program(
}

// Get the ID and display it.
struct bpf_prog_info info;
struct bpf_prog_info info = {};
uint32_t info_size = sizeof(info);
if (bpf_obj_get_info_by_fd(program_fd, &info, &info_size) < 0) {
std::cerr << "error " << errno << ": loaded program but could not get ID" << std::endl;
Expand Down Expand Up @@ -273,7 +273,7 @@ _unpin_program_by_id(ebpf_id_t id)
if (fd < 0) {
continue;
}
bpf_prog_info info;
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) {
Expand All @@ -299,7 +299,7 @@ _find_object_with_program(ebpf_id_t id)
bpf_object__for_each_program(program, *object)
{
int program_fd = bpf_program__fd(program);
struct bpf_prog_info info;
struct bpf_prog_info info = {};
uint32_t info_size = sizeof(info);
if (bpf_obj_get_info_by_fd(program_fd, &info, &info_size) < 0) {
continue;
Expand Down Expand Up @@ -387,7 +387,7 @@ _ebpf_program_attach_by_id(ebpf_id_t program_id, ebpf_attach_type_t attach_type,
}

if (interface_parameter != nullptr) {
struct bpf_prog_info info;
struct bpf_prog_info info = {};
uint32_t info_size = sizeof(info);
if (bpf_obj_get_info_by_fd(program_fd, &info, &info_size) < 0) {
result = EBPF_INVALID_ARGUMENT;
Expand Down Expand Up @@ -677,7 +677,7 @@ handle_ebpf_show_programs(
}
program_fd = bpf_prog_get_fd_by_id(program_id);

struct bpf_prog_info info;
struct bpf_prog_info info = {};
uint32_t info_size = (uint32_t)sizeof(info);
int error = bpf_obj_get_info_by_fd(program_fd, &info, &info_size);
if (error < 0) {
Expand Down Expand Up @@ -746,6 +746,25 @@ handle_ebpf_show_programs(
std::cout << "Program type : " << program_type_name << "\n";
std::cout << "Mode : " << execution_type_name << "\n";
std::cout << "# map IDs : " << info.nr_map_ids << "\n";

if (info.nr_map_ids > 0) {
ebpf_id_t* map_ids = (ebpf_id_t*)malloc(info.nr_map_ids * sizeof(ebpf_id_t));
if (map_ids == nullptr) {
break;
}
info.map_ids = (uintptr_t)map_ids;
error = bpf_obj_get_info_by_fd(program_fd, &info, &info_size);
if (error < 0) {
break;
}
std::cout << "map IDs : " << map_ids[0] << "\n";
for (uint32_t i = 1; i < info.nr_map_ids; i++) {
std::cout << " " << map_ids[i] << "\n";
}

free(map_ids);
}

std::cout << "# pinned paths : " << info.pinned_path_count << "\n";
std::cout << "# links : " << info.link_count << "\n";
}
Expand Down
4 changes: 2 additions & 2 deletions libs/execution_context/ebpf_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -1508,7 +1508,7 @@ _ebpf_core_protocol_get_object_info(
result = ebpf_map_get_info((ebpf_map_t*)object, reply->info, &info_size);
break;
case EBPF_OBJECT_PROGRAM:
result = ebpf_program_get_info((ebpf_program_t*)object, reply->info, &info_size);
result = ebpf_program_get_info((ebpf_program_t*)object, request->info, reply->info, &info_size);
break;
}

Expand Down Expand Up @@ -1975,7 +1975,7 @@ static ebpf_protocol_handler_t _ebpf_protocol_handlers[] = {
DECLARE_PROTOCOL_HANDLER_FIXED_REQUEST_FIXED_REPLY(get_next_link_id, PROTOCOL_ALL_MODES),
DECLARE_PROTOCOL_HANDLER_FIXED_REQUEST_FIXED_REPLY(get_next_map_id, PROTOCOL_ALL_MODES),
DECLARE_PROTOCOL_HANDLER_FIXED_REQUEST_FIXED_REPLY(get_next_program_id, PROTOCOL_ALL_MODES),
DECLARE_PROTOCOL_HANDLER_FIXED_REQUEST_VARIABLE_REPLY(get_object_info, info, PROTOCOL_ALL_MODES),
DECLARE_PROTOCOL_HANDLER_VARIABLE_REQUEST_VARIABLE_REPLY(get_object_info, info, info, PROTOCOL_ALL_MODES),
DECLARE_PROTOCOL_HANDLER_VARIABLE_REQUEST_VARIABLE_REPLY(
get_next_pinned_program_path, start_path, next_path, PROTOCOL_ALL_MODES),
DECLARE_PROTOCOL_HANDLER_FIXED_REQUEST_NO_REPLY(bind_map, PROTOCOL_ALL_MODES),
Expand Down
1 change: 1 addition & 0 deletions libs/execution_context/ebpf_link.c
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ ebpf_link_get_info(
EBPF_RETURN_RESULT(EBPF_INSUFFICIENT_BUFFER);
}

memset(info, 0, sizeof(*info));
info->id = link->object.id;
info->prog_id = (link->program) ? ((ebpf_core_object_t*)link->program)->id : EBPF_ID_NONE;
info->type = link->link_type;
Expand Down
7 changes: 7 additions & 0 deletions libs/execution_context/ebpf_maps.c
Original file line number Diff line number Diff line change
Expand Up @@ -2221,6 +2221,7 @@ ebpf_map_get_info(
info->key_size = map->ebpf_map_definition.key_size;
info->value_size = map->original_value_size;
info->max_entries = map->ebpf_map_definition.max_entries;
info->map_flags = 0;
if (info->type == BPF_MAP_TYPE_ARRAY_OF_MAPS || info->type == BPF_MAP_TYPE_HASH_OF_MAPS) {
ebpf_core_object_map_t* object_map = EBPF_FROM_FIELD(ebpf_core_object_map_t, core_map, map);
info->inner_map_id =
Expand Down Expand Up @@ -2308,3 +2309,9 @@ ebpf_map_peek_entry(_In_ ebpf_map_t* map, size_t value_size, _Out_writes_(value_
memcpy(value, return_value, map->ebpf_map_definition.value_size);
return EBPF_SUCCESS;
}

ebpf_id_t
ebpf_map_get_id(_In_ const ebpf_map_t* map)
{
return map->object.id;
}
8 changes: 8 additions & 0 deletions libs/execution_context/ebpf_maps.h
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,14 @@ extern "C"
ebpf_result_t
ebpf_map_peek_entry(_In_ ebpf_map_t* map, size_t value_size, _Out_writes_(value_size) uint8_t* value, int flags);

/**
* @brief Get the ID of a given map.
*
* @param[in] map Map to get ID of.
* @returns Map ID.
*/
ebpf_id_t
ebpf_map_get_id(_In_ const ebpf_map_t* map);
#ifdef __cplusplus
}
#endif
Loading

0 comments on commit 568e8f0

Please sign in to comment.