ebpf-for-windows/libs/ebpfnetsh/programs.cpp

840 строки
28 KiB
C++

// Copyright (c) Microsoft Corporation
// SPDX-License-Identifier: MIT
#include "bpf/bpf.h"
#include "bpf/libbpf.h"
#include "platform.h"
#include "programs.h"
#include "tokens.h"
#include "utilities.h"
#include <iomanip>
#include <set>
#include <string>
#include <vector>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <combaseapi.h>
#include <netsh.h>
typedef enum
{
BC_ANY = 0,
BC_YES = 1,
BC_NO = 2,
} BOOLEAN_CONSTRAINT;
static TOKEN_VALUE _boolean_constraint_enum[] = {
{L"any", BC_ANY},
{L"yes", BC_YES},
{L"no", BC_NO},
};
static TOKEN_VALUE _ebpf_execution_type_enum[] = {
{L"jit", EBPF_EXECUTION_JIT},
{L"interpret", EBPF_EXECUTION_INTERPRET},
};
typedef enum
{
PT_NONE, // Don't pin any programs in an eBPF object.
PT_FIRST, // Pin only the first program in an object.
PT_ALL, // Pin all programs in an object.
} pinned_type_t;
static TOKEN_VALUE _ebpf_pinned_type_enum[] = {
{L"none", PT_NONE},
{L"first", PT_FIRST},
{L"all", PT_ALL},
};
std::vector<struct bpf_object*> _ebpf_netsh_objects;
bool
_prog_type_supports_interface(bpf_prog_type prog_type)
{
return (prog_type == BPF_PROG_TYPE_XDP);
}
bool
_prog_type_supports_compartment(bpf_prog_type prog_type)
{
return (prog_type == BPF_PROG_TYPE_CGROUP_SOCK_ADDR);
}
_Must_inspect_result_ ebpf_result_t
_process_interface_parameter(
_In_ const _Null_terminated_ wchar_t* interface_parameter, bpf_prog_type prog_type, _Out_ uint32_t* if_index)
{
ebpf_result_t result = EBPF_SUCCESS;
if (_prog_type_supports_interface(prog_type)) {
result = parse_if_index(interface_parameter, if_index);
if (result != EBPF_SUCCESS) {
std::cerr << "Interface parameter is invalid." << std::endl;
}
} else {
std::cerr << "Interface parameter is not allowed for program types that don't support interfaces." << std::endl;
result = EBPF_INVALID_ARGUMENT;
}
return result;
}
_Must_inspect_result_ ebpf_result_t
_process_compartment_parameter(
_In_ const _Null_terminated_ wchar_t* compartment_parameter,
bpf_prog_type prog_type,
_Out_ COMPARTMENT_ID* compartment_id)
{
ebpf_result_t result = EBPF_SUCCESS;
if (_prog_type_supports_compartment(prog_type)) {
*compartment_id = (COMPARTMENT_ID)_wtoi(compartment_parameter);
if (*compartment_id == 0) {
std::cerr << "Compartment parameter is invalid." << std::endl;
result = EBPF_INVALID_ARGUMENT;
}
} else {
std::cerr << "Compartment parameter is not allowed for program types that don't support compartments."
<< std::endl;
result = EBPF_INVALID_ARGUMENT;
}
return result;
}
struct _program_unloader
{
struct bpf_object* object;
~_program_unloader() { bpf_object__close(object); }
};
struct _link_deleter
{
struct bpf_link* link;
~_link_deleter()
{
if (link != nullptr) {
ebpf_link_close(link);
}
}
};
// The following function uses windows specific input type to match
// definition of "FN_HANDLE_CMD" in public file of NetSh.h
unsigned long
handle_ebpf_add_program(
LPCWSTR machine, LPWSTR* argv, DWORD current_index, DWORD argc, DWORD flags, LPCVOID data, BOOL* done)
{
UNREFERENCED_PARAMETER(machine);
UNREFERENCED_PARAMETER(flags);
UNREFERENCED_PARAMETER(data);
UNREFERENCED_PARAMETER(done);
TAG_TYPE tags[] = {
{TOKEN_FILENAME, NS_REQ_PRESENT, FALSE},
{TOKEN_TYPE, NS_REQ_ZERO, FALSE},
{TOKEN_PINPATH, NS_REQ_ZERO, FALSE},
{TOKEN_INTERFACE, NS_REQ_ZERO, FALSE},
{TOKEN_PINNED, NS_REQ_ZERO, FALSE},
{TOKEN_EXECUTION, NS_REQ_ZERO, FALSE},
{TOKEN_COMPARTMENT, NS_REQ_ZERO, FALSE}};
const int FILENAME_INDEX = 0;
const int TYPE_INDEX = 1;
const int PINPATH_INDEX = 2;
const int INTERFACE_INDEX = 3;
const int PINNED_INDEX = 4;
const int EXECUTION_INDEX = 5;
const int COMPARTMENT_INDEX = 6;
unsigned long tag_type[_countof(tags)] = {0};
unsigned long status =
PreprocessCommand(nullptr, argv, current_index, argc, tags, _countof(tags), 0, _countof(tags), tag_type);
std::string filename;
std::string pinpath;
bpf_prog_type prog_type = BPF_PROG_TYPE_UNSPEC;
bpf_attach_type attach_type = BPF_ATTACH_TYPE_UNSPEC;
pinned_type_t pinned_type = PT_FIRST; // Like bpftool, we default to pin first.
ebpf_execution_type_t execution = EBPF_EXECUTION_JIT;
wchar_t* interface_parameter = nullptr;
wchar_t* compartment_parameter = nullptr;
for (int i = 0; (status == NO_ERROR) && ((i + current_index) < argc); i++) {
switch (tag_type[i]) {
case FILENAME_INDEX: {
filename = down_cast_from_wstring(std::wstring(argv[current_index + i]));
break;
}
case TYPE_INDEX: {
std::string type_name = down_cast_from_wstring(std::wstring(argv[current_index + i]));
if (libbpf_prog_type_by_name(type_name.c_str(), &prog_type, &attach_type) < 0) {
status = ERROR_INVALID_PARAMETER;
}
break;
}
case INTERFACE_INDEX: {
interface_parameter = argv[current_index + i];
break;
}
case PINPATH_INDEX:
pinpath = down_cast_from_wstring(std::wstring(argv[current_index + i]));
break;
case PINNED_INDEX:
status = MatchEnumTag(
NULL,
argv[current_index + i],
_countof(_ebpf_pinned_type_enum),
_ebpf_pinned_type_enum,
(unsigned long*)&pinned_type);
break;
case EXECUTION_INDEX:
status = MatchEnumTag(
NULL,
argv[current_index + i],
_countof(_ebpf_execution_type_enum),
_ebpf_execution_type_enum,
(unsigned long*)&execution);
break;
case COMPARTMENT_INDEX:
compartment_parameter = argv[current_index + i];
break;
default:
status = ERROR_INVALID_SYNTAX;
break;
}
}
if (status != NO_ERROR) {
return status;
}
struct bpf_object* object;
int program_fd;
PCSTR error_message;
object = bpf_object__open(filename.c_str());
if (object == nullptr) {
std::cerr << "error " << errno << ": could not open file" << std::endl;
return ERROR_SUPPRESS_OUTPUT;
}
struct bpf_program* program = bpf_object__next_program(object, nullptr);
if (prog_type != BPF_PROG_TYPE_UNSPEC) {
bpf_program__set_type(program, prog_type);
}
if (bpf_object__load(object) < 0) {
std::cerr << "error " << errno << ": could not load program" << std::endl;
size_t error_message_size;
error_message = bpf_program__log_buf(program, &error_message_size);
if (error_message != nullptr) {
std::cerr << error_message << std::endl;
}
bpf_object__close(object);
return ERROR_SUPPRESS_OUTPUT;
}
program_fd = bpf_program__fd(program);
// Program loaded. Populate the unloader with object pointer, such that
// the program gets unloaded automatically, if this function fails somewhere.
struct _program_unloader unloader = {object};
ebpf_result_t result;
uint32_t if_index;
void* attach_parameters = nullptr;
size_t attach_parameters_size = 0;
if (interface_parameter != nullptr) {
result = _process_interface_parameter(interface_parameter, bpf_program__type(program), &if_index);
if (result == EBPF_SUCCESS) {
attach_parameters = &if_index;
attach_parameters_size = sizeof(if_index);
} else {
return ERROR_SUPPRESS_OUTPUT;
}
}
COMPARTMENT_ID compartment_id = UNSPECIFIED_COMPARTMENT_ID;
if (compartment_parameter != nullptr) {
result = _process_compartment_parameter(compartment_parameter, bpf_program__type(program), &compartment_id);
if (result == EBPF_SUCCESS) {
attach_parameters = &compartment_id;
attach_parameters_size = sizeof(compartment_id);
} else {
return ERROR_SUPPRESS_OUTPUT;
}
}
struct bpf_link* link;
result = ebpf_program_attach(program, nullptr, attach_parameters, attach_parameters_size, &link);
if (result != EBPF_SUCCESS) {
std::cerr << "error " << result << ": could not attach program" << std::endl;
return ERROR_SUPPRESS_OUTPUT;
}
// Link attached. Populate the deleter with link pointer, such that link
// object is closed when the function returns.
struct _link_deleter link_deleter = {link};
if (pinned_type == PT_FIRST) {
// The pinpath specified is like a "file" under which to pin programs.
// This matches the "bpftool prog load" behavior.
if (pinpath.empty()) {
pinpath = bpf_program__name(program);
}
if (bpf_program__pin(program, pinpath.c_str()) < 0) {
std::cerr << "error " << errno << ": could not pin to " << pinpath << std::endl;
return ERROR_SUPPRESS_OUTPUT;
}
} else if (pinned_type == PT_ALL) {
// The pinpath specified is like a "directory" under which to pin programs.
// This matches the "bpftool prog loadall" behavior.
if (bpf_object__pin_programs(object, pinpath.c_str()) < 0) {
std::cerr << "error " << errno << ": could not pin to " << pinpath << std::endl;
return ERROR_SUPPRESS_OUTPUT;
}
}
// Get the ID and display it.
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;
return ERROR_SUPPRESS_OUTPUT;
}
std::cout << "Loaded with ID " << info.id << std::endl;
unloader.object = nullptr;
_ebpf_netsh_objects.push_back(object);
return ERROR_SUCCESS;
}
// Given a program ID, unpin the program from all paths to which
// it is currently pinned.
static unsigned long
_unpin_program_by_id(ebpf_id_t id)
{
ebpf_result_t result;
unsigned long status = NO_ERROR;
// Read all pin paths. Currently we get them in a non-deterministic
// order, so we use a std::set to sort them in code point order.
char pinpath[EBPF_MAX_PIN_PATH_LENGTH] = "";
std::set<std::string> paths;
while (ebpf_get_next_pinned_program_path(pinpath, pinpath) == EBPF_SUCCESS) {
paths.insert(pinpath);
}
// Now walk through all paths in code point order.
for (auto path : paths) {
int fd = bpf_obj_get(path.c_str());
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(path.c_str());
if (result != EBPF_SUCCESS) {
printf("Error %d unpinning %d from %s\n", result, id, path.c_str());
status = ERROR_SUPPRESS_OUTPUT;
} else {
printf("Unpinned %d from %s\n", id, path.c_str());
}
}
}
Platform::_close(fd);
}
return status;
}
static std::vector<struct bpf_object*>::const_iterator
_find_object_with_program(ebpf_id_t id)
{
for (auto object = _ebpf_netsh_objects.begin(); object != _ebpf_netsh_objects.end(); object++) {
bpf_program* program;
bpf_object__for_each_program(program, *object)
{
int program_fd = bpf_program__fd(program);
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;
}
if (info.id == id) {
return object;
}
}
}
return _ebpf_netsh_objects.end();
}
// The following function uses windows specific type to match
// definition of "FN_HANDLE_CMD" in public file of NetSh.h
unsigned long
handle_ebpf_delete_program(
LPCWSTR machine, LPWSTR* argv, DWORD current_index, DWORD argc, DWORD flags, LPCVOID data, BOOL* done)
{
UNREFERENCED_PARAMETER(machine);
UNREFERENCED_PARAMETER(flags);
UNREFERENCED_PARAMETER(data);
UNREFERENCED_PARAMETER(done);
TAG_TYPE tags[] = {
{TOKEN_ID, NS_REQ_PRESENT, FALSE},
};
const int ID_INDEX = 0;
unsigned long tag_type[_countof(tags)] = {0};
unsigned long status =
PreprocessCommand(nullptr, argv, current_index, argc, tags, _countof(tags), 0, _countof(tags), tag_type);
ebpf_id_t id = EBPF_ID_NONE;
for (int i = 0; (status == NO_ERROR) && ((i + current_index) < argc) && (i < _countof(tag_type)); i++) {
switch (tag_type[i]) {
case ID_INDEX: {
id = (uint32_t)_wtoi(argv[current_index + i]);
break;
}
default:
status = ERROR_INVALID_SYNTAX;
break;
}
}
if (status != NO_ERROR) {
return status;
}
int program_fd = bpf_prog_get_fd_by_id(id);
if (program_fd == ebpf_fd_invalid) {
std::cout << "Program not found\n";
return ERROR_SUPPRESS_OUTPUT;
}
Platform::_close(program_fd);
// 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.
std::vector<struct bpf_object*>::const_iterator object = _find_object_with_program(id);
if (object != _ebpf_netsh_objects.end()) {
bpf_object__close(*object);
_ebpf_netsh_objects.erase(object);
}
// 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 NO_ERROR;
}
_Must_inspect_result_ ebpf_result_t
_ebpf_program_attach_by_id(
ebpf_id_t program_id, ebpf_attach_type_t attach_type, _In_opt_z_ const wchar_t* interface_parameter)
{
ebpf_result_t result = EBPF_SUCCESS;
uint32_t if_index;
void* attach_parameters = nullptr;
size_t attach_parameters_size = 0;
fd_t program_fd = bpf_prog_get_fd_by_id(program_id);
if (program_fd < 0) {
return EBPF_INVALID_ARGUMENT;
}
if (interface_parameter != nullptr) {
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;
} else {
result = _process_interface_parameter(interface_parameter, info.type, &if_index);
if (result == EBPF_SUCCESS) {
attach_parameters = &if_index;
attach_parameters_size = sizeof(if_index);
}
}
}
struct bpf_link* link;
if (result == EBPF_SUCCESS) {
ebpf_result_t local_result =
ebpf_program_attach_by_fd(program_fd, &attach_type, attach_parameters, attach_parameters_size, &link);
if (local_result == EBPF_SUCCESS) {
ebpf_link_close(link);
}
}
Platform::_close(program_fd);
return result;
}
int // errno value
_ebpf_program_detach_by_id(ebpf_id_t program_id)
{
// Use the same APIs as bpftool.
uint32_t link_id = 0;
while (bpf_link_get_next_id(link_id, &link_id) == 0) {
fd_t link_fd = bpf_link_get_fd_by_id(link_id);
if (link_fd < 0) {
continue;
}
struct bpf_link_info link_info;
uint32_t info_len = sizeof(link_info);
if (bpf_obj_get_info_by_fd(link_fd, &link_info, &info_len) == 0) {
if (link_info.prog_id == program_id) {
if (bpf_link_detach(link_fd) < 0) {
return errno;
}
Platform::_close(link_fd);
return NO_ERROR;
}
}
Platform::_close(link_fd);
}
return ERROR_NOT_FOUND;
}
// The following function uses windows specific type as an input to match
// definition of "FN_HANDLE_CMD" in public file of NetSh.h
unsigned long
handle_ebpf_set_program(
LPCWSTR machine, LPWSTR* argv, DWORD current_index, DWORD argc, DWORD flags, LPCVOID data, BOOL* done)
{
UNREFERENCED_PARAMETER(machine);
UNREFERENCED_PARAMETER(flags);
UNREFERENCED_PARAMETER(data);
UNREFERENCED_PARAMETER(done);
TAG_TYPE tags[] = {
{TOKEN_ID, NS_REQ_PRESENT, FALSE},
{TOKEN_ATTACHED, NS_REQ_ZERO, FALSE},
{TOKEN_PINPATH, NS_REQ_ZERO, FALSE},
{TOKEN_INTERFACE, NS_REQ_ZERO, FALSE}};
const int ID_INDEX = 0;
const int ATTACHED_INDEX = 1;
const int PINPATH_INDEX = 2;
const int INTERFACE_INDEX = 3;
unsigned long tag_type[_countof(tags)] = {0};
wchar_t* interface_parameter = nullptr;
unsigned long status = PreprocessCommand(
nullptr,
argv,
current_index,
argc,
tags,
_countof(tags),
0,
3, // Two required tags plus at least one optional tag.
tag_type);
uint32_t id = 0;
std::string pinpath;
ebpf_attach_type_t attach_type = EBPF_ATTACH_TYPE_UNSPECIFIED;
for (int i = 0; (status == NO_ERROR) && ((i + current_index) < argc); i++) {
switch (tag_type[i]) {
case ID_INDEX: {
id = (uint32_t)_wtoi(argv[current_index + i]);
break;
}
case ATTACHED_INDEX: {
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 INTERFACE_INDEX: {
interface_parameter = argv[current_index + i];
break;
}
case PINPATH_INDEX:
pinpath = down_cast_from_wstring(std::wstring(argv[current_index + i]));
break;
default:
status = ERROR_INVALID_SYNTAX;
break;
}
}
if (status != NO_ERROR) {
return status;
}
if (tags[ATTACHED_INDEX].bPresent) {
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, interface_parameter);
if (result != NO_ERROR) {
std::cerr << "error " << result << ": could not attach program" << std::endl;
return ERROR_SUPPRESS_OUTPUT;
}
} else {
int error = _ebpf_program_detach_by_id(id);
if (error != NO_ERROR) {
std::cerr << "error " << error << ": could not detach program" << std::endl;
return ERROR_SUPPRESS_OUTPUT;
}
}
}
if (tags[PINPATH_INDEX].bPresent) {
if (pinpath.empty()) {
// Unpin a program from all names to which it is currently pinpath.
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);
if (program_fd == ebpf_fd_invalid) {
std::cerr << "Program not found." << std::endl;
return ERROR_SUPPRESS_OUTPUT;
}
status = bpf_obj_pin(program_fd, pinpath.c_str());
if (status != EBPF_SUCCESS) {
std::cerr << "error " << status << ": could not pin program" << std::endl;
return ERROR_SUPPRESS_OUTPUT;
}
Platform::_close(program_fd);
}
}
return ERROR_OKAY;
}
// The following function uses windows specific type as an input to match
// definition of "FN_HANDLE_CMD" in public file of NetSh.h
unsigned long
handle_ebpf_show_programs(
LPCWSTR machine, LPWSTR* argv, DWORD current_index, DWORD argc, DWORD flags, LPCVOID data, BOOL* done)
{
UNREFERENCED_PARAMETER(machine);
UNREFERENCED_PARAMETER(flags);
UNREFERENCED_PARAMETER(data);
UNREFERENCED_PARAMETER(done);
TAG_TYPE tags[] = {
{TOKEN_TYPE, NS_REQ_ZERO, FALSE},
{TOKEN_ATTACHED, NS_REQ_ZERO, FALSE},
{TOKEN_PINNED, NS_REQ_ZERO, FALSE},
{TOKEN_LEVEL, NS_REQ_ZERO, FALSE},
{TOKEN_FILENAME, NS_REQ_ZERO, FALSE},
{TOKEN_SECTION, NS_REQ_ZERO, FALSE},
{TOKEN_ID, NS_REQ_ZERO, FALSE},
};
const int TYPE_INDEX = 0;
const int ATTACHED_INDEX = 1;
const int PINNED_INDEX = 2;
const int LEVEL_INDEX = 3;
const int FILENAME_INDEX = 4;
const int SECTION_INDEX = 5;
const int ID_INDEX = 6;
unsigned long tag_type[_countof(tags)] = {0};
unsigned long status =
PreprocessCommand(nullptr, argv, current_index, argc, tags, _countof(tags), 0, _countof(tags), tag_type);
ebpf_program_type_t program_type = EBPF_PROGRAM_TYPE_UNSPECIFIED;
BOOLEAN_CONSTRAINT attached = BC_ANY;
BOOLEAN_CONSTRAINT pinned = BC_ANY;
VERBOSITY_LEVEL level = VL_NORMAL;
std::string filename;
std::string section;
ebpf_id_t id = 0;
for (int i = 0; (status == NO_ERROR) && ((i + current_index) < argc); i++) {
switch (tag_type[i]) {
case TYPE_INDEX: {
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);
if (result != EBPF_SUCCESS) {
status = ERROR_INVALID_PARAMETER;
}
break;
}
case ATTACHED_INDEX:
status = MatchEnumTag(
NULL,
argv[current_index + i],
_countof(_boolean_constraint_enum),
_boolean_constraint_enum,
(unsigned long*)&attached);
if (status != NO_ERROR) {
status = ERROR_INVALID_PARAMETER;
}
break;
case PINNED_INDEX:
status = MatchEnumTag(
NULL,
argv[current_index + i],
_countof(_boolean_constraint_enum),
_boolean_constraint_enum,
(unsigned long*)&pinned);
if (status != NO_ERROR) {
status = ERROR_INVALID_PARAMETER;
}
break;
case LEVEL_INDEX:
status =
MatchEnumTag(NULL, argv[current_index + i], _countof(g_LevelEnum), g_LevelEnum, (unsigned long*)&level);
if (status != NO_ERROR) {
status = ERROR_INVALID_PARAMETER;
}
break;
case FILENAME_INDEX: {
filename = down_cast_from_wstring(std::wstring(argv[current_index + i]));
break;
}
case SECTION_INDEX: {
section = down_cast_from_wstring(std::wstring(argv[current_index + i]));
break;
}
case ID_INDEX: {
id = (uint32_t)_wtoi(argv[current_index + i]);
break;
}
default:
status = ERROR_INVALID_SYNTAX;
break;
}
}
if (status != NO_ERROR) {
return status;
}
// If the user specified an ID and no level, default to verbose.
if (tags[ID_INDEX].bPresent && !tags[LEVEL_INDEX].bPresent) {
level = VL_VERBOSE;
}
if (level == VL_NORMAL) {
std::cout << "\n";
std::cout << " ID Pins Links Mode Type Name\n";
std::cout << "====== ==== ===== ========= ============= ====================\n";
}
uint32_t program_id = 0;
fd_t program_fd = ebpf_fd_invalid;
for (;;) {
const char* program_file_name;
const char* program_section_name;
const char* execution_type_name;
ebpf_execution_type_t program_execution_type;
uint32_t next_program_id;
if (bpf_prog_get_next_id(program_id, &next_program_id) < 0) {
break;
}
program_id = next_program_id;
if (program_fd != ebpf_fd_invalid) {
Platform::_close(program_fd);
}
program_fd = bpf_prog_get_fd_by_id(program_id);
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) {
break;
}
if ((id != 0) && (info.id != id)) {
continue;
}
if (tags[0].bPresent && (memcmp(&info.type_uuid, &program_type, sizeof(program_type)) != 0)) {
continue;
}
// Filter by attached if desired.
if (attached == BC_NO && info.link_count > 0) {
continue;
}
if (attached == BC_YES && info.link_count == 0) {
continue;
}
// Filter by pinpath if desired.
if (pinned == BC_NO && info.pinned_path_count > 0) {
continue;
}
if (pinned == BC_YES && info.pinned_path_count == 0) {
continue;
}
ebpf_result_t result =
ebpf_program_query_info(program_fd, &program_execution_type, &program_file_name, &program_section_name);
if (result != EBPF_SUCCESS) {
continue;
}
if (filename.empty() || strcmp(program_file_name, filename.c_str()) == 0) {
if (section.empty() || strcmp(program_section_name, section.c_str()) == 0) {
switch (program_execution_type) {
case EBPF_EXECUTION_JIT:
execution_type_name = "JIT";
break;
case EBPF_EXECUTION_INTERPRET:
execution_type_name = "INTERPRET";
break;
default:
execution_type_name = "NATIVE";
break;
}
const char* program_type_name = ebpf_get_program_type_name(&info.type_uuid);
if (level == VL_NORMAL) {
printf(
"%6u %4u %5u %-9s %-13s %s\n",
info.id,
info.pinned_path_count,
info.link_count,
execution_type_name,
program_type_name,
info.name);
} else {
std::cout << "\n";
std::cout << "ID : " << info.id << "\n";
std::cout << "File name : " << program_file_name << "\n";
std::cout << "Section : " << program_section_name << "\n";
std::cout << "Name : " << info.name << "\n";
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) {
std::vector<ebpf_id_t> map_ids(info.nr_map_ids);
info.map_ids = (uintptr_t)map_ids.data();
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";
}
}
std::cout << "# pinned paths : " << info.pinned_path_count << "\n";
std::cout << "# links : " << info.link_count << "\n";
}
}
}
ebpf_free_string(program_file_name);
ebpf_free_string(program_section_name);
}
if (program_fd != ebpf_fd_invalid) {
Platform::_close(program_fd);
}
return status;
}