Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rename ebpf_execution_context to ebpf_state #6

Merged
merged 1 commit into from
Mar 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/run-execution-context-tests.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Run unit tests of execution context
name: Run unit tests of ebpf_state

on:
push:
Expand All @@ -22,7 +22,7 @@ jobs:
- name: Build targets
run: |
CC=clang-15 CXX=clang++-15 cmake -S. -Bbuild -DCMAKE_BUILD_TYPE:STRING=Release -G Ninja
CC=clang-15 CXX=clang++-15 cmake --build ./build --config Release --target ebpf_execution_context_test
CC=clang-15 CXX=clang++-15 cmake --build ./build --config Release --target ebpf_state_test
- name: Run tests
run: |
./build/execution-test/ebpf_execution_context_test
./build/execution-test/ebpf_state_test
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@ Add this directory into your CMake project, and links target `libebpf`. See `lib
- `pip install -r requirements.txt`
- `pytest`

## How to run execution context tests?
- Build CMake target `ebpf_execution_context_test`
- Run `./build/execution-test/ebpf_execution_context_test`
## How to run ebpf_state tests?
- Build CMake target `ebpf_state_test`
- Run `./build/execution-test/ebpf_state_test`

## Notes on ebpf_execution_context
## Notes on ebpf_state

`ebpf_vm` is just a virtual machine, it doesn't have ability to manage or access maps. We provide `ebpf_execution_context` for such operations. You may create a `ebpf_execution_context` and create maps in it. Before running your ebpf programs using ebpf_vm, you should call `ebpf_execution_context__setup_internal_helpers` to setup internal helpers (such as `bpf_map_lookup_elem`) to the vm, and should also point `ebpf_execution_context__thread_global_context` to the context you want to use. Then you can run your ebpf programs with access to the context.
`ebpf_vm` is just a virtual machine, it doesn't have ability to manage or access maps. We provide `ebpf_state` for such operations. You may create a `ebpf_state` and create maps in it. Before running your ebpf programs using ebpf_vm, you should call `ebpf_state__setup_internal_helpers` to setup internal helpers (such as `bpf_map_lookup_elem`) to the vm, and should also point `ebpf_state__thread_global_context` to the context you want to use. Then you can run your ebpf programs with access to the context.

## Notes on FFI
You may call `ebpf_execution_context__register_ffi_function` to register a FFI function. FFI functions are type safe, which means that it always accept int64_t and returns int64_t at the bpf side, but it would convert the received argument to the proper types when calling the FFI function.
You may use some helper macros for FFI callling in the BPF side. see `libebpf_ffi.bpf.c` for details.
You may use `LIBEBPF_EXPORT_FUNCTION_ARG[N]` (where `[N]` is an integer range 0 to 6) to export a function and automatically registers when the execution context was created. This macro is only applicatable in the target `libebpf`, since it needs a modified link process
You may call `ebpf_state__register_ffi_function` to register a FFI function. FFI functions are type safe, which means that it always accept int64_t and returns int64_t at the bpf side, but it would convert the received argument to the proper types when calling the FFI function.
You may use some helper macros for FFI callling in the BPF side. see `libebpf_ffi.bpf.h` for details.
You may use `LIBEBPF_EXPORT_FUNCTION_ARG[N]` (where `[N]` is an integer range 0 to 6) to export a function and automatically registers when the ebpf_state was created. This macro is only applicatable in the target `libebpf`, since it needs a modified link process
14 changes: 7 additions & 7 deletions execution-test/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
add_subdirectory(catch2)

add_executable(
ebpf_execution_context_test
ebpf_state_test
./test_map.cpp
./test_with_ebpf.cpp
./test_ffi.cpp
)

add_dependencies(ebpf_execution_context_test
add_dependencies(ebpf_state_test
Catch2
libebpf
)
target_link_libraries(ebpf_execution_context_test
target_link_libraries(ebpf_state_test
PRIVATE
libebpf
Catch2::Catch2WithMain
)

target_include_directories(ebpf_execution_context_test PRIVATE ${Catch2_INCLUDE} ${CMAKE_CURRENT_SOURCE_DIR}/../src)
target_include_directories(ebpf_state_test PRIVATE ${Catch2_INCLUDE} ${CMAKE_CURRENT_SOURCE_DIR}/../src)

set_target_properties(ebpf_execution_context_test PROPERTIES CXX_STANDARD 20 LINKER_LANGUAGE CXX)
target_compile_options(ebpf_execution_context_test PRIVATE -fsanitize=address -fsanitize=undefined -D_LIBEBPF_UNIT_TEST)
target_link_options(ebpf_execution_context_test PRIVATE -fsanitize=address -fsanitize=undefined)
set_target_properties(ebpf_state_test PROPERTIES CXX_STANDARD 20 LINKER_LANGUAGE CXX)
target_compile_options(ebpf_state_test PRIVATE -fsanitize=address -fsanitize=undefined -D_LIBEBPF_UNIT_TEST)
target_link_options(ebpf_state_test PRIVATE -fsanitize=address -fsanitize=undefined)
14 changes: 7 additions & 7 deletions execution-test/test_ffi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,29 +18,29 @@ static uint64_t uint64_plus(uint64_t a, uint64_t b) {
return a + b;
}
TEST_CASE("Test FFI calls, without eBPF program") {
std::unique_ptr<ebpf_execution_context_t, decltype(&ebpf_execution_context__destroy)> ctx(ebpf_execution_context__create(),
ebpf_execution_context__destroy);
std::unique_ptr<ebpf_state_t, decltype(&ebpf_state__destroy)> ctx(ebpf_state__create(),
ebpf_state__destroy);
REQUIRE(ctx != nullptr);
ebpf_execution_context__thread_global_context = ctx.get();
ebpf_state__thread_global_state = ctx.get();
std::unique_ptr<ebpf_vm_t, decltype(&ebpf_vm_destroy)> vm(ebpf_vm_create(), ebpf_vm_destroy);
REQUIRE(vm != nullptr);
ebpf_execution_context__setup_internal_helpers(vm.get());
ebpf_state__setup_internal_helpers(vm.get());
int uint32_plus_id;
{
enum libebpf_ffi_type args[6] = { ARG_UINT32, ARG_UINT32, ARG_VOID, ARG_VOID, ARG_VOID, ARG_VOID };
uint32_plus_id = ebpf_execution_context__register_ffi_function(ctx.get(), (void *)&uint32_plus, "uint32_plus", args, ARG_UINT32);
uint32_plus_id = ebpf_state__register_ffi(ctx.get(), (void *)&uint32_plus, "uint32_plus", args, ARG_UINT32);
REQUIRE(uint32_plus_id >= 0);
}
int uint64_plus_id;
{
enum libebpf_ffi_type args[6] = { ARG_UINT64, ARG_UINT64, ARG_VOID, ARG_VOID, ARG_VOID, ARG_VOID };
uint64_plus_id = ebpf_execution_context__register_ffi_function(ctx.get(), (void *)&uint64_plus, "uint64_plus", args, ARG_UINT64);
uint64_plus_id = ebpf_state__register_ffi(ctx.get(), (void *)&uint64_plus, "uint64_plus", args, ARG_UINT64);
REQUIRE(uint64_plus_id >= 0);
}
int int32_plus_id;
{
enum libebpf_ffi_type args[6] = { ARG_INT32, ARG_INT32, ARG_VOID, ARG_VOID, ARG_VOID, ARG_VOID };
int32_plus_id = ebpf_execution_context__register_ffi_function(ctx.get(), (void *)&int32_plus, "int32_plus", args, ARG_INT32);
int32_plus_id = ebpf_state__register_ffi(ctx.get(), (void *)&int32_plus, "int32_plus", args, ARG_INT32);
REQUIRE(int32_plus_id >= 0);
}
auto libebpf_ffi_lookup_by_name = [&](const char *name) -> int {
Expand Down
58 changes: 29 additions & 29 deletions execution-test/test_map.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@ struct hashmap_value {
};

TEST_CASE("Test hash map") {
ebpf_execution_context_t *ctx = ebpf_execution_context__create();
ebpf_state_t *ctx = ebpf_state__create();
REQUIRE(ctx != nullptr);
struct ebpf_map_attr attr {
.type = EBPF_MAP_TYPE_HASH, .key_size = 4, .value_size = sizeof(hashmap_value), .max_ents = 100, .flags = 0,
};
int id = ebpf_execution_context__map_create(ctx, "my_map", &attr);
int id = ebpf_state__map_create(ctx, "my_map", &attr);
REQUIRE(id >= 0);
// Insert
for (int i = 1; i <= 10; i++) {
Expand All @@ -38,7 +38,7 @@ TEST_CASE("Test hash map") {
};
std::string str = std::to_string(i);
strcpy(val.c, str.c_str());
REQUIRE(ebpf_execution_context__map_elem_update(ctx, id, &i, &val, 0) == 0);
REQUIRE(ebpf_state__map_elem_update(ctx, id, &i, &val, 0) == 0);
}
// Query
std::vector<int> vec;
Expand All @@ -49,7 +49,7 @@ TEST_CASE("Test hash map") {
std::shuffle(vec.begin(), vec.end(), gen);
for (int x : vec) {
hashmap_value out_buf;
REQUIRE(ebpf_execution_context__map_elem_lookup(ctx, id, &x, &out_buf) == 0);
REQUIRE(ebpf_state__map_elem_lookup(ctx, id, &x, &out_buf) == 0);
REQUIRE(out_buf.a == x);
REQUIRE(out_buf.b == ((((uint64_t)x) << 32) | x));
std::string str = std::to_string(x);
Expand All @@ -59,21 +59,21 @@ TEST_CASE("Test hash map") {
// Delete the first 3 elements
for (int i = 0; i < 3; i++) {
int x = vec[i];
REQUIRE(ebpf_execution_context__map_elem_delete(ctx, id, &x) == 0);
REQUIRE(ebpf_state__map_elem_delete(ctx, id, &x) == 0);
}
// Check if we deleted successfully?
for (int i = 0; i < 3; i++) {
int x = vec[i];
hashmap_value buf;
REQUIRE(ebpf_execution_context__map_elem_lookup(ctx, id, &x, &buf) == -ENOENT);
REQUIRE(ebpf_state__map_elem_lookup(ctx, id, &x, &buf) == -ENOENT);
}
// Iterate over the remained elements
std::map<int, hashmap_value> map;
int *key = nullptr;
int next_key;
while (ebpf_execution_context__map_get_next_key(ctx, id, key, &next_key) == 0) {
while (ebpf_state__map_get_next_key(ctx, id, key, &next_key) == 0) {
hashmap_value buf;
REQUIRE(ebpf_execution_context__map_elem_lookup(ctx, id, &next_key, &buf) == 0);
REQUIRE(ebpf_state__map_elem_lookup(ctx, id, &next_key, &buf) == 0);
map[next_key] = buf;
key = &next_key;
}
Expand All @@ -88,35 +88,35 @@ TEST_CASE("Test hash map") {
std::string str = std::to_string(x);
REQUIRE(str == val.c);
}
REQUIRE(ebpf_execution_context__map_destroy(ctx, id) == 0);
ebpf_execution_context__destroy(ctx);
REQUIRE(ebpf_state__map_destroy(ctx, id) == 0);
ebpf_state__destroy(ctx);
}

TEST_CASE("Test array map") {
ebpf_execution_context_t *ctx = ebpf_execution_context__create();
ebpf_state_t *ctx = ebpf_state__create();
REQUIRE(ctx != nullptr);
struct ebpf_map_attr attr {
.type = EBPF_MAP_TYPE_ARRAY, .key_size = 4, .value_size = 8, .max_ents = 100, .flags = 0,
};
int id = ebpf_execution_context__map_create(ctx, "my_map", &attr);
int id = ebpf_state__map_create(ctx, "my_map", &attr);
REQUIRE(id >= 0);

for (uint32_t i = 0; i < 100; i++) {
uint64_t x = ((uint64_t)i << 32) | i;
REQUIRE(ebpf_execution_context__map_elem_update(ctx, id, &i, &x, 0) == 0);
REQUIRE(ebpf_state__map_elem_update(ctx, id, &i, &x, 0) == 0);
}
uint32_t key;
REQUIRE(ebpf_execution_context__map_get_next_key(ctx, id, nullptr, &key) == 0);
REQUIRE(ebpf_state__map_get_next_key(ctx, id, nullptr, &key) == 0);
REQUIRE(key == 0);
for (uint32_t i = 0; i < 99; i++) {
REQUIRE(ebpf_execution_context__map_get_next_key(ctx, id, &i, &key) == 0);
REQUIRE(ebpf_state__map_get_next_key(ctx, id, &i, &key) == 0);
REQUIRE(key == i + 1);
}
key = 99;
REQUIRE(ebpf_execution_context__map_get_next_key(ctx, id, &key, &key) == -ENOENT);
REQUIRE(ebpf_execution_context__map_destroy(ctx, id) == 0);
REQUIRE(ebpf_execution_context__map_get_next_key(ctx, id, &key, &key) < 0);
ebpf_execution_context__destroy(ctx);
REQUIRE(ebpf_state__map_get_next_key(ctx, id, &key, &key) == -ENOENT);
REQUIRE(ebpf_state__map_destroy(ctx, id) == 0);
REQUIRE(ebpf_state__map_get_next_key(ctx, id, &key, &key) < 0);
ebpf_state__destroy(ctx);
}

static int handle_event(void *ctx, void *data, int len) {
Expand All @@ -133,12 +133,12 @@ bool operator<(const std::vector<uint8_t> &a, const std::vector<uint8_t> &b) {
}

TEST_CASE("Test ringbuf map") {
ebpf_execution_context_t *ctx = ebpf_execution_context__create();
ebpf_state_t *ctx = ebpf_state__create();
REQUIRE(ctx != nullptr);
struct ebpf_map_attr attr {
.type = EBPF_MAP_TYPE_RINGBUF, .max_ents = 256 * 1024, .flags = 0,
};
int id = ebpf_execution_context__map_create(ctx, "my_map", &attr);
int id = ebpf_state__map_create(ctx, "my_map", &attr);
REQUIRE(id >= 0);

std::mt19937 gen;
Expand All @@ -154,7 +154,7 @@ TEST_CASE("Test ringbuf map") {
curr.push_back(rand_bytes(gen));
rand_data.push_back(curr);
}
auto priv_data = ebpf_execution_context__get_ringbuf_map_private_data(ctx, id);
auto priv_data = ebpf_state__get_ringbuf_map_private_data(ctx, id);
REQUIRE(priv_data != nullptr);
auto producer = std::thread([=]() {
std::vector<void *> buffers;
Expand Down Expand Up @@ -194,29 +194,29 @@ TEST_CASE("Test ringbuf map") {
producer.join();
consumer.join();

REQUIRE(ebpf_execution_context__map_destroy(ctx, id) == 0);
REQUIRE(ebpf_state__map_destroy(ctx, id) == 0);

ebpf_execution_context__destroy(ctx);
ebpf_state__destroy(ctx);
}

TEST_CASE("Test hashmap kernel helpers") {
ebpf_execution_context_t *ctx = ebpf_execution_context__create();
ebpf_state_t *ctx = ebpf_state__create();
REQUIRE(ctx != nullptr);
struct ebpf_map_attr attr {
.type = EBPF_MAP_TYPE_HASH, .key_size = 4, .value_size = sizeof(hashmap_value), .max_ents = 100, .flags = 0,
};
int id = ebpf_execution_context__map_create(ctx, "my_map", &attr);
int id = ebpf_state__map_create(ctx, "my_map", &attr);
REQUIRE(id >= 0);
{
int key = 111;
hashmap_value value{ .a = 233, .b = 456, .c = "aaaa" };
REQUIRE(ebpf_execution_context__map_elem_update(ctx, id, &key, &value, 0) == 0);
REQUIRE(ebpf_state__map_elem_update(ctx, id, &key, &value, 0) == 0);
}
int key = 111;
hashmap_value *buf = (hashmap_value *)ctx->map_ops[EBPF_MAP_TYPE_HASH].elem_lookup_from_helper(ctx->maps[id], &key);
REQUIRE(buf != nullptr);
REQUIRE(ebpf_execution_context__map_elem_delete(ctx, id, &key) == 0);
REQUIRE(ebpf_state__map_elem_delete(ctx, id, &key) == 0);
buf->a = 112;
buf->b = 233;
ebpf_execution_context__destroy(ctx);
ebpf_state__destroy(ctx);
}
16 changes: 8 additions & 8 deletions execution-test/test_with_ebpf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,21 @@
#include <ostream>

TEST_CASE("Test map operations with ebpf programs") {
std::unique_ptr<ebpf_execution_context_t, decltype(&ebpf_execution_context__destroy)> ctx(ebpf_execution_context__create(),
ebpf_execution_context__destroy);
std::unique_ptr<ebpf_state_t, decltype(&ebpf_state__destroy)> ctx(ebpf_state__create(),
ebpf_state__destroy);
REQUIRE(ctx != nullptr);
std::unique_ptr<ebpf_vm_t, decltype(&ebpf_vm_destroy)> vm(ebpf_vm_create(), ebpf_vm_destroy);
REQUIRE(vm != nullptr);
struct ebpf_map_attr attr {
.type = EBPF_MAP_TYPE_HASH, .key_size = 8, .value_size = 8, .max_ents = 10, .flags = 0,
};
int hash_map_id = ebpf_execution_context__map_create(ctx.get(), "hash_map", &attr);
int hash_map_id = ebpf_state__map_create(ctx.get(), "hash_map", &attr);
uint64_t key = 1, value = 233;
REQUIRE(ebpf_execution_context__map_elem_update(ctx.get(), hash_map_id, &key, &value, 0) == 0);
REQUIRE(ebpf_state__map_elem_update(ctx.get(), hash_map_id, &key, &value, 0) == 0);
key = 2;
value = 456;
REQUIRE(ebpf_execution_context__map_elem_update(ctx.get(), hash_map_id, &key, &value, 0) == 0);
ebpf_execution_context__setup_internal_helpers(vm.get());
REQUIRE(ebpf_state__map_elem_update(ctx.get(), hash_map_id, &key, &value, 0) == 0);
ebpf_state__setup_internal_helpers(vm.get());

REQUIRE(hash_map_id >= 0);
struct libebpf_insn insns[] = { // r1 = map_by_fd(hash_map_id)
Expand Down Expand Up @@ -68,10 +68,10 @@ TEST_CASE("Test map operations with ebpf programs") {
BPF_RAW_INSN(BPF_CLASS_JMP | BPF_JMP_EXIT | BPF_SOURCE_IMM, 0, 0, 0, 0)
};
REQUIRE(ebpf_vm_load_instructions(vm.get(), insns, std::size(insns)) == 0);
ebpf_execution_context__thread_global_context = ctx.get();
ebpf_state__thread_global_state = ctx.get();
uint64_t ret;
REQUIRE(ebpf_vm_run(vm.get(), nullptr, 0, &ret) == 0);
key = 3;
REQUIRE(ebpf_execution_context__map_elem_lookup(ctx.get(), hash_map_id, &key, &value) == 0);
REQUIRE(ebpf_state__map_elem_lookup(ctx.get(), hash_map_id, &key, &value) == 0);
REQUIRE(value == 233 + 456);
}
27 changes: 13 additions & 14 deletions include/libebpf_execution.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,41 +7,40 @@ extern "C" {
#endif

/**
* @brief Opaque type for an execution context.
* Execution context provides interfaces to create & operate on maps, defining FFI interfaces, and run ebpf programs using ebpf_vm. eBPF programs
* executed through an execution context could use helpers related to maps and FFI.
* @brief Opaque type for an ebpf_state.
* ebpf_state provides interfaces to create & operate on maps, defining FFI interfaces, and provides helpers for ebpf_vm to do these operations.
*/
struct ebpf_execution_context;
struct ebpf_state;

typedef struct ebpf_execution_context ebpf_execution_context_t;
typedef struct ebpf_state ebpf_state_t;

/**
* @brief Global context for the current thread. You must set it before run your ebpf program, if you want to use features provided by
* ebpf_execution_context
* ebpf_state
*
*/
extern __thread ebpf_execution_context_t *ebpf_execution_context__thread_global_context;
extern __thread ebpf_state_t *ebpf_state__thread_global_state;

/**
* @brief Create an execution context
* @brief Create an ebpf_state
*
* @return ebpf_execution_context_t* A pointer to the context if succeeded. NULL if failed. Call ebpf_error_string to get error details.
* @return ebpf_state_t* A pointer to the context if succeeded. NULL if failed. Call ebpf_error_string to get error details.
*/
ebpf_execution_context_t *ebpf_execution_context__create();
ebpf_state_t *ebpf_state__create();

/**
* @brief Destroy the execution context
* @brief Destroy the ebpf_state
*
* @param ctx Pointer to the context
*/
void ebpf_execution_context__destroy(ebpf_execution_context_t *ctx);
void ebpf_state__destroy(ebpf_state_t *ctx);

/**
* @brief Setup helpers provided by ebpf_execution_context for the given ebpf_vm
* @brief Setup helpers provided by ebpf_state for the given ebpf_vm
*
* @param vm The vm instance
*/
void ebpf_execution_context__setup_internal_helpers(ebpf_vm_t *vm);
void ebpf_state__setup_internal_helpers(ebpf_vm_t *vm);
#ifdef __cplusplus
}
#endif
Expand Down
2 changes: 1 addition & 1 deletion include/libebpf_ffi.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ struct libebpf_ffi_function {
* @param return_value_type Return type of the given FFI function
* @return Negative value if failed. Otherwise the function ID
*/
int ebpf_execution_context__register_ffi_function(ebpf_execution_context_t *ctx, void *func, const char *name,
int ebpf_state__register_ffi(ebpf_state_t *ctx, void *func, const char *name,
const enum libebpf_ffi_type arg_types[6], enum libebpf_ffi_type return_value_type);
#ifdef __cplusplus
}
Expand Down
Loading
Loading