Skip to content

Commit

Permalink
Add ELFIO as submodule
Browse files Browse the repository at this point in the history
Add support for passing ELF file to plugin

Signed-off-by: Alan Jowett <alanjo@microsoft.com>
  • Loading branch information
Alan-Jowett committed Jan 3, 2023
1 parent 3432dc3 commit 71279ac
Show file tree
Hide file tree
Showing 11 changed files with 417 additions and 101 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "external/elfio"]
path = external/elfio
url = https://github.com/serge1/ELFIO.git
1 change: 1 addition & 0 deletions external/elfio
Submodule elfio added at 6fc23e
27 changes: 27 additions & 0 deletions include/bpf_conformance.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,30 @@ bpf_conformance(
bpf_conformance_list_instructions_t list_instructions_option =
bpf_conformance_list_instructions_t::LIST_INSTRUCTIONS_NONE,
bool debug = false);

typedef struct _bpf_conformance_options
{
std::optional<std::string> include_test_regex;
std::optional<std::string> exclude_test_regex;
bpf_conformance_test_CPU_version_t CPU_version;
bpf_conformance_list_instructions_t list_instructions_option;
bool debug;
bool xdp_prolog;
bool elf_format;
} bpf_conformance_options_t;

/**
* @brief Run the BPF conformance tests with the given plugin.
*
* @param[in] test_files List of test files to run.
* @param[in] plugin_path The path to the plugin to run the tests with.
* @param[in] plugin_options The options to pass to the plugin.
* @param[in] options Options controlling the behavior of the tests.
* @return The test results for each test file.
*/
std::map<std::filesystem::path, std::tuple<bpf_conformance_test_result_t, std::string>>
bpf_conformance_options(
const std::vector<std::filesystem::path>& test_files,
const std::filesystem::path& plugin_path,
const std::vector<std::string>& plugin_options,
const bpf_conformance_options_t& options);
140 changes: 77 additions & 63 deletions libbpf_plugin/libbpf_plugin.cc
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#define bpf_stats_type bpf_stats_type_fake
enum bpf_stats_type_fake {};
#include <bpf/bpf.h>
#include <bpf/libbpf.h>
#undef bpf_stats_type_fake

/**
Expand Down Expand Up @@ -56,43 +57,79 @@ bytes_to_ebpf_inst(std::vector<uint8_t> bytes)
return instructions;
}

/**
* @brief Create a prolog that loads the packet memory into R1 and the lenght into R2.
*
* @param[in] size Expected size of the packet.
* @return Vector of bpf_insn that represents the prolog.
*/
std::vector<bpf_insn>
generate_xdp_prolog(int size)
{
// Create a prolog that converts the BPF program to one that can be loaded
// at the XDP attach point.
// This involves:
// 1. Copying the ctx->data into r1.
// 2. Copying the ctx->data_end - ctx->data into r2.
// 3. Satisfying the verifier that r2 is the length of the packet.
return {
{0xb7, 0x0, 0x0, 0x0, -1}, // mov64 r0, -1
{0xbf, 0x6, 0x1, 0x0, 0x0}, // mov r6, r1
{0x61, 0x1, 0x6, 0x0, 0x0}, // ldxw r1, [r6+0]
{0x61, 0x2, 0x6, 0x4, 0x0}, // ldxw r2, [r6+4]
{0xbf, 0x3, 0x1, 0x0, 0x0}, // mov r3, r1
{0x7, 0x3, 0x0, 0x0, size}, // add r3, size
{0xbd, 0x3, 0x2, 0x1, 0x0}, // jle r3, r2, +1
{0x95, 0x0, 0x0, 0x0, 0x0}, // exit
{0xb7, 0x2, 0x0, 0x0, size}, // mov r2, size
// Decode BPF instructions from program string and load them into the kernel.
int load_bpf_instructions(const std::string& program_string, size_t memory_length, std::string& log) {
std::vector<bpf_insn> program = bytes_to_ebpf_inst(base16_decode(program_string));

// Load program into kernel.
constexpr uint32_t log_size = 1024;
log.resize(log_size);
#ifdef USE_DEPRECATED_LOAD_PROGRAM
int fd = bpf_load_program(
BPF_PROG_TYPE_XDP,
reinterpret_cast<const bpf_insn*>(program.data()),
static_cast<uint32_t>(program.size()),
"MIT",
0,
&log[0],
log_size);
#else
bpf_prog_load_opts opts{
.sz = sizeof(opts),
.attempts = 1,
.expected_attach_type = BPF_XDP,
.log_size = log_size,
.log_buf = &log[0],
};
int fd = bpf_prog_load(
BPF_PROG_TYPE_XDP,
"conformance_test",
"MIT",
reinterpret_cast<const bpf_insn*>(program.data()),
program.size(),
&opts);
#endif
return fd;
}

int load_elf_file(const std::string& file_contents, std::string& log)
{
std::vector<uint8_t> bytes = base16_decode(file_contents);
struct bpf_object* object = nullptr;
struct bpf_program* program = nullptr;
struct bpf_map* map = nullptr;
int fd = -1;
int error = 0;
bool result = false;

// Load ELF file from memory
object = bpf_object__open_mem(bytes.data(), bytes.size(), nullptr);
if (!object) {
log = "Failed to load ELF file";
return -1;
}

// Load BPF program
bpf_object__for_each_program(program, object) {
error = bpf_program__load(program, (char*)"MIT", 0);
if (error) {
return -1;
}
return bpf_program__fd(program);
}
return -1;
}

/**
* @brief This program reads BPF instructions from stdin and memory contents from
* the first agument. It then executes the BPF program and prints the
* the first argument. It then executes the BPF program and prints the
* value of r0 at the end of execution.
*/
int
main(int argc, char** argv)
{
bool debug = false;
bool elf = false;
std::vector<std::string> args(argv, argv + argc);
if (args.size() > 0) {
args.erase(args.begin());
Expand All @@ -101,7 +138,7 @@ main(int argc, char** argv)
std::string memory_string;

if (args.size() > 0 && args[0] == "--help") {
std::cout << "usage: " << argv[0] << " [--program <base16 program bytes>] [<base16 memory bytes>] [--debug]" << std::endl;
std::cout << "usage: " << argv[0] << " [--program <base16 program bytes>] [<base16 memory bytes>] [--debug] [--elf]" << std::endl;
return 1;
}

Expand All @@ -114,7 +151,7 @@ main(int argc, char** argv)
}

// Next parameter is optional memory contents.
if (args.size() > 0 && args[0] != "--debug") {
if (args.size() > 0 && !args[0].starts_with("--")) {
memory_string = args[0];
args.erase(args.begin());
}
Expand All @@ -124,49 +161,26 @@ main(int argc, char** argv)
args.erase(args.begin());
}

if (args.size() > 0 && args[0] == "--elf") {
elf = true;
args.erase(args.begin());
}

if (args.size() > 0 && args[0].size() > 0) {
std::cerr << "Unexpected arguments: " << args[0] << std::endl;
return 1;
}

std::vector<bpf_insn> program = bytes_to_ebpf_inst(base16_decode(program_string));
std::vector<uint8_t> memory = base16_decode(memory_string);

// Add prolog if program accesses memory.
if (memory.size() > 0) {
auto prolog_instructions = generate_xdp_prolog(memory.size());
program.insert(program.begin(), prolog_instructions.begin(), prolog_instructions.end());
std::string log;
int fd = -1;
if (!elf) {
fd = load_bpf_instructions(program_string, memory.size(), log);
}
else {
fd = load_elf_file(program_string, log);
}

// Load program into kernel.
constexpr uint32_t log_size = 1024;
std::string log;
log.resize(log_size);
#ifdef USE_DEPRECATED_LOAD_PROGRAM
int fd = bpf_load_program(
BPF_PROG_TYPE_XDP,
reinterpret_cast<const bpf_insn*>(program.data()),
static_cast<uint32_t>(program.size()),
"MIT",
0,
&log[0],
log_size);
#else
bpf_prog_load_opts opts{
.sz = sizeof(opts),
.attempts = 1,
.expected_attach_type = BPF_XDP,
.log_size = log_size,
.log_buf = &log[0],
};
int fd = bpf_prog_load(
BPF_PROG_TYPE_XDP,
"conformance_test",
"MIT",
reinterpret_cast<const bpf_insn*>(program.data()),
program.size(),
&opts);
#endif
if (fd < 0) {
if (debug)
std::cout << "Failed to load program: " << log << std::endl;
Expand Down
Loading

0 comments on commit 71279ac

Please sign in to comment.