/* * Copyright (c) 2017 Politecnico di Torino * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include "BPF.h" #include "catch.hpp" TEST_CASE("test bpf table", ebpf::bpf_module_rw_engine_enabled() ? "[bpf_table]" : "[bpf_table][!mayfail]") { const std::string BPF_PROGRAM = R"( BPF_TABLE("hash", int, int, myhash, 128); )"; auto bpf = std::make_unique(); ebpf::StatusTuple res(0); std::vector> elements; res = bpf->init(BPF_PROGRAM); REQUIRE(res.ok()); ebpf::BPFTable t = bpf->get_table("myhash"); // update element std::string value; res = t.update_value("0x07", "0x42"); REQUIRE(res.ok()); res = t.get_value("0x7", value); REQUIRE(res.ok()); REQUIRE(value == "0x42"); // update another element res = t.update_value("0x11", "0x777"); REQUIRE(res.ok()); res = t.get_value("0x11", value); REQUIRE(res.ok()); REQUIRE(value == "0x777"); // remove value res = t.remove_value("0x11"); REQUIRE(res.ok()); res = t.get_value("0x11", value); REQUIRE(!res.ok()); res = t.update_value("0x15", "0x888"); REQUIRE(res.ok()); res = t.get_table_offline(elements); REQUIRE(res.ok()); REQUIRE(elements.size() == 2); // check that elements match what is in the table for (auto &it : elements) { if (it.first == "0x15") { REQUIRE(it.second == "0x888"); } else if (it.first == "0x7") { REQUIRE(it.second == "0x42"); } else { FAIL("Element " + it.first + " should not be on the table", it.first); } } res = t.clear_table_non_atomic(); REQUIRE(res.ok()); res = t.get_table_offline(elements); REQUIRE(res.ok()); REQUIRE(elements.size() == 0); // delete bpf_module, call to key/leaf printf/scanf must fail bpf.reset(); res = t.update_value("0x07", "0x42"); REQUIRE(!res.ok()); res = t.get_value("0x07", value); REQUIRE(!res.ok()); res = t.remove_value("0x07"); REQUIRE(!res.ok()); } #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 6, 0) TEST_CASE("test bpf percpu tables", ebpf::bpf_module_rw_engine_enabled() ? "[bpf_percpu_table]" : "[bpf_percpu_table][!mayfail]") { const std::string BPF_PROGRAM = R"( BPF_PERCPU_HASH(myhash, int, u64, 128); )"; ebpf::BPF bpf; ebpf::StatusTuple res(0); res = bpf.init(BPF_PROGRAM); REQUIRE(res.ok()); ebpf::BPFTable t = bpf.get_table("myhash"); size_t ncpus = ebpf::BPFTable::get_possible_cpu_count(); std::vector v1(ncpus); for (size_t i = 0; i < ncpus; i++) { v1.at(i) = std::to_string(42 * i); } // update element std::vector value; res = t.update_value("0x07", v1); REQUIRE(res.ok()); res = t.get_value("0x07", value); REQUIRE(res.ok()); for (size_t i = 0; i < ncpus; i++) { REQUIRE(42 * i == std::stoul(value.at(i), nullptr, 16)); } } #endif TEST_CASE("test bpf hash table", "[bpf_hash_table]") { const std::string BPF_PROGRAM = R"( BPF_HASH(myhash, int, int, 128); )"; ebpf::BPF bpf; ebpf::StatusTuple res(0); res = bpf.init(BPF_PROGRAM); REQUIRE(res.ok()); auto t = bpf.get_hash_table("myhash"); int key, value; // updaate element key = 0x08; value = 0x43; res = t.update_value(key, value); REQUIRE(res.ok()); REQUIRE(t[key] == value); // update another element key = 0x12; value = 0x778; res = t.update_value(key, value); REQUIRE(res.ok()); key = 0x31; value = 0x123; res = t.update_value(key, value); REQUIRE(res.ok()); key = 0x12; value = 0; res = t.get_value(key, value); REQUIRE(res.ok()); REQUIRE(value == 0x778); // remove value and dump table key = 0x12; res = t.remove_value(key); REQUIRE(res.ok()); auto values = t.get_table_offline(); REQUIRE(values.size() == 2); // clear table res = t.clear_table_non_atomic(); REQUIRE(res.ok()); values = t.get_table_offline(); REQUIRE(values.size() == 0); } TEST_CASE("test bpf stack table", "[bpf_stack_table]") { #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 6, 0) const std::string BPF_PROGRAM = R"( BPF_HASH(id, int, int, 1); BPF_STACK_TRACE(stack_traces, 8); int on_sys_getuid(void *ctx) { int stack_id = stack_traces.get_stackid(ctx, BPF_F_REUSE_STACKID); int zero = 0, *val; val = id.lookup_or_try_init(&zero, &stack_id); if (val) { (*val) = stack_id; } return 0; } )"; ebpf::BPF bpf; ebpf::StatusTuple res(0); res = bpf.init(BPF_PROGRAM); REQUIRE(res.ok()); std::string getuid_fnname = bpf.get_syscall_fnname("getuid"); res = bpf.attach_kprobe(getuid_fnname, "on_sys_getuid"); REQUIRE(res.ok()); REQUIRE(getuid() >= 0); res = bpf.detach_kprobe(getuid_fnname); REQUIRE(res.ok()); auto id = bpf.get_hash_table("id"); auto stack_traces = bpf.get_stack_table("stack_traces"); int stack_id = id[0]; REQUIRE(stack_id >= 0); auto addrs = stack_traces.get_stack_addr(stack_id); auto symbols = stack_traces.get_stack_symbol(stack_id, -1); REQUIRE(addrs.size() > 0); REQUIRE(addrs.size() == symbols.size()); bool found = false; for (const auto &symbol : symbols) if (symbol.find("sys_getuid") != std::string::npos) { found = true; break; } REQUIRE(found); stack_traces.clear_table_non_atomic(); addrs = stack_traces.get_stack_addr(stack_id); REQUIRE(addrs.size() == 0); #endif } TEST_CASE("test bpf stack_id table", "[bpf_stack_table]") { #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0) const std::string BPF_PROGRAM = R"( BPF_HASH(id, int, int, 1); BPF_STACK_TRACE_BUILDID(stack_traces, 8); int on_sys_getuid(void *ctx) { int stack_id = stack_traces.get_stackid(ctx, BPF_F_USER_STACK); int zero = 0, *val; val = id.lookup_or_try_init(&zero, &stack_id); if (val) { (*val) = stack_id; } return 0; } )"; ebpf::BPF bpf; ebpf::StatusTuple res(0); res = bpf.init(BPF_PROGRAM); REQUIRE(res.ok()); std::string getuid_fnname = bpf.get_syscall_fnname("getuid"); res = bpf.attach_kprobe(getuid_fnname, "on_sys_getuid"); REQUIRE(res.ok()); REQUIRE(getuid() >= 0); res = bpf.detach_kprobe(getuid_fnname); REQUIRE(res.ok()); auto id = bpf.get_hash_table("id"); auto stack_traces = bpf.get_stackbuildid_table("stack_traces"); /* libc locations on different distributions are added below*/ bpf.add_module("/lib/x86_64-linux-gnu/libc.so.6"); //Location of libc in ubuntu bpf.add_module("/lib64/libc.so.6"); //Location of libc fedora machine int stack_id = id[0]; REQUIRE(stack_id >= 0); auto addrs = stack_traces.get_stack_addr(stack_id); auto symbols = stack_traces.get_stack_symbol(stack_id); REQUIRE(addrs.size() > 0); REQUIRE(addrs.size() == symbols.size()); bool found = false; for (const auto &symbol : symbols) { if (symbol.find("getuid") != std::string::npos) { found = true; break; } } REQUIRE(found); stack_traces.clear_table_non_atomic(); addrs = stack_traces.get_stack_addr(stack_id); REQUIRE(addrs.size()==0); #endif }