-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
ebpf unit testing -- handle tailcalls and support user-space map emul…
…ation This commit contains a demo on how to handle tailcalls and also supports user-space eBPF map emulation which is a part of our fake kernel model library. We resolve tailcalls by mocking the tailcall helper function and stubbing a callback since tailcalls are achieved by the tailcall helper function. In this way, the callback will be called instead whenever the tailcall helper function is encountered. We also provide a library to support user-space map emulation. Since eBPF maps are maintained by the kernel, we do not actually know if the map operations are safe or not without testing in the actual kernel. Considering that eBPF maps are actually hashmaps, we create a user-space hashmap and emulate the eBPF map operations in the user space via callbacks. new file: bpf/mock/fake_maps.h: wrap up raw hashmap operations. bpf/tests/drop_notify_test.h: include a demo test showing how to handle tailcalls test/bpf/drop_notify_test.c: contains main function to run test functions in drop_notify_test.h modified: bpf/mock/Dockerfile: add hashmap library bpf/tests/nat_test.h: add a demo test showing how to use user-space map emulation test/bpf/nat-test.c: add the new test in bpf/tests/nat_test.h into main function bpf/mock/Makefile: put all the generated files into folder "mocks" for later cleaning bpf/mock/mock_helpers.sh: put all the generated files into folder "mocks" for later cleaning test/bpf/Makefile: clean bpf/mock/mocks Signed-off-by: Xinyuan Zhang <zhangxinyuan@google.com>
- Loading branch information
Showing
9 changed files
with
230 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
/* SPDX-License-Identifier: GPL-2.0 */ | ||
/* Copyright (C) 2021 Authors of Cilium */ | ||
|
||
// library for user-space map emulation | ||
// It emulates the eBPF map operations in the user space by wrapping up the raw | ||
// hashmap functions. | ||
|
||
#include "hashmap.h" | ||
|
||
typedef HASHMAP(void, void) hashmap_void_t; | ||
|
||
|
||
// Get the number of entries inside map. | ||
size_t fake_get_size(hashmap_void_t *map) { | ||
return hashmap_size(map); | ||
} | ||
|
||
// Initiate map with given length of keys and capacity. | ||
void fake_init_map(hashmap_void_t *map, size_t (*hash)(const void *key), int (*compare)(const void *a, const void *b)) { | ||
hashmap_init(map, hash, compare); | ||
} | ||
|
||
// Loop up the value of given key. | ||
void *fake_lookup_elem(hashmap_void_t *map, void *key) { | ||
return hashmap_get(map, key); | ||
} | ||
|
||
// Update the value of given key. | ||
int fake_update_elem(hashmap_void_t *map, void *key, void *value, __u32 flags, size_t size) { | ||
void *entry = hashmap_get(map, key); | ||
if (entry == NULL && hashmap_size(map) >= size) { | ||
printf("Update failed: Map is full\n"); | ||
return -1; | ||
} | ||
|
||
int r = hashmap_put(map, key, value); | ||
if (r == -EEXIST) { | ||
if (flags == BPF_NOEXIST) return -1; | ||
hashmap_remove(map, key); | ||
hashmap_put(map, key, value); | ||
return 0; | ||
} else if (r == 0) { | ||
if (flags == BPF_EXIST) { | ||
hashmap_remove(map, key); | ||
return -1; | ||
} | ||
return 0; | ||
} | ||
|
||
return r; | ||
} | ||
|
||
// Delete given key. | ||
int fake_delete_elem(hashmap_void_t *map, void *key) { | ||
return hashmap_remove(map, key); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
#!/usr/bin/env bash | ||
|
||
ruby ../../CMock/lib/cmock.rb -obpf.yaml helpers.h | ||
ruby ../../CMock/lib/cmock.rb -obpf.yaml helpers_skb.h | ||
ruby ../../CMock/lib/cmock.rb -obpf.yaml helpers_xdp.h | ||
ruby ../../CMock/lib/cmock.rb -obpf.yaml mocks/helpers.h | ||
ruby ../../CMock/lib/cmock.rb -obpf.yaml mocks/helpers_skb.h | ||
ruby ../../CMock/lib/cmock.rb -obpf.yaml mocks/helpers_xdp.h |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
/* SPDX-License-Identifier: GPL-2.0 */ | ||
/* Copyright (C) 2021 Authors of Cilium */ | ||
|
||
// sample unit test program for some functions in drop.h | ||
// It contains function definitions for testing "send_drop_notify" and "__send_drop_notify". | ||
// It is used to perform unit test on the above two functions to demonstrate how | ||
// to handle tailcalls. | ||
// There is a tailcall at the end of function send_drop_notif which actually | ||
// calls function __send_drop_notify. We can stub the tailcall and actually call | ||
// the function with callback. | ||
// If other functions in drop.h need to be tested, please add the function definitions at the bottom. | ||
|
||
#define __BPF_HELPERS_SKB__ | ||
#define __BPF_HELPERS__ | ||
#define DROP_NOTIFY | ||
|
||
#include <stdio.h> | ||
#include <assert.h> | ||
|
||
// Include unity test framework and all the mock libraries. | ||
#include "unity.h" | ||
#include "mocks/mock_helpers.h" | ||
#include "mocks/mock_helpers_skb.h" | ||
|
||
#include "bpf/ctx/skb.h" | ||
#include "node_config.h" | ||
|
||
// Include lib/metrics.h which contains the definition of ep_tail_call first to | ||
// avoid it to be included again in lib/drop.h. | ||
#include "lib/metrics.h" | ||
|
||
// Define macros like the followings to make sure the original tailcall is redirected | ||
// to the mock tailcall function, the last 0 does not matter because we do not | ||
// actually use the arguments. | ||
#define ep_tail_call(a, b) tail_call(a, b, 0) | ||
|
||
// The file containing the functions to be tested must be included after | ||
// defining the above macros. | ||
#include "lib/drop.h" | ||
|
||
// Undefine ep_tail_call to stop redirecting to the mock. It is not necessary | ||
// unless you would like to include something else that might conflict with the | ||
// redirection. | ||
#undef ep_tail_call | ||
|
||
|
||
// This is the function we use as the callback when stubbing the tailcall. | ||
void __send_drop_notify_tailcall(void* ctx, const void* map, __u32 index, int cmock_num_calls) { | ||
|
||
// We can even unit-test the function which is actually called by the tailcall | ||
// within the callback. | ||
skb_event_output_IgnoreAndReturn(0); | ||
assert(!__send_drop_notify(ctx)); | ||
} | ||
|
||
// A sample test for function send_drop_notify | ||
// It is a demo to show how we handle tailcalls. | ||
void test_send_drop_notify() { | ||
struct __ctx_buff ctx; | ||
|
||
// Set the expectations for the helpers functions called before the tailcall. | ||
map_lookup_elem_IgnoreAndReturn(NULL); | ||
map_update_elem_IgnoreAndReturn(0); | ||
|
||
// We stub the tailcall here by calling callback. | ||
tail_call_Stub(__send_drop_notify_tailcall); | ||
assert(!send_drop_notify(&ctx, 0, 0, 0, 0, 0, 0)); | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
/* SPDX-License-Identifier: GPL-2.0 */ | ||
/* Copyright (C) 2021 Authors of Cilium */ | ||
|
||
// source file for drop_notify_test.h. | ||
// It contains contains main functions to run test functions drop_notify_test.h. | ||
// It is used to perform unit test on functions in drop.h. | ||
|
||
#include "tests/drop_notify_test.h" | ||
|
||
void setUp(void) { | ||
// set stuff up here | ||
} | ||
|
||
void tearDown(void) { | ||
// clean stuff up here | ||
} | ||
|
||
|
||
int main(int argc, char *argv[]) | ||
{ | ||
test_send_drop_notify(); | ||
return 0; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters