| /* |
| * 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 <linux/version.h> |
| #include <unistd.h> |
| #include <string> |
| |
| #include "BPF.h" |
| #include "catch.hpp" |
| |
| TEST_CASE("test bpf table", "[bpf_table]") { |
| const std::string BPF_PROGRAM = R"( |
| BPF_TABLE("hash", int, int, myhash, 128); |
| )"; |
| |
| ebpf::BPF *bpf(new ebpf::BPF); |
| ebpf::StatusTuple res(0); |
| std::vector<std::pair<std::string, std::string>> elements; |
| res = bpf->init(BPF_PROGRAM); |
| REQUIRE(res.code() == 0); |
| |
| ebpf::BPFTable t = bpf->get_table("myhash"); |
| |
| // update element |
| std::string value; |
| res = t.update_value("0x07", "0x42"); |
| REQUIRE(res.code() == 0); |
| res = t.get_value("0x7", value); |
| REQUIRE(res.code() == 0); |
| REQUIRE(value == "0x42"); |
| |
| // update another element |
| res = t.update_value("0x11", "0x777"); |
| REQUIRE(res.code() == 0); |
| res = t.get_value("0x11", value); |
| REQUIRE(res.code() == 0); |
| REQUIRE(value == "0x777"); |
| |
| // remove value |
| res = t.remove_value("0x11"); |
| REQUIRE(res.code() == 0); |
| res = t.get_value("0x11", value); |
| REQUIRE(res.code() != 0); |
| |
| res = t.update_value("0x15", "0x888"); |
| REQUIRE(res.code() == 0); |
| res = t.get_table_offline(elements); |
| REQUIRE(res.code() == 0); |
| 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.code() == 0); |
| res = t.get_table_offline(elements); |
| REQUIRE(res.code() == 0); |
| REQUIRE(elements.size() == 0); |
| |
| // delete bpf_module, call to key/leaf printf/scanf must fail |
| delete bpf; |
| |
| res = t.update_value("0x07", "0x42"); |
| REQUIRE(res.code() != 0); |
| |
| res = t.get_value("0x07", value); |
| REQUIRE(res.code() != 0); |
| |
| res = t.remove_value("0x07"); |
| REQUIRE(res.code() != 0); |
| } |
| |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 6, 0) |
| TEST_CASE("test bpf percpu tables", "[bpf_percpu_table]") { |
| const std::string BPF_PROGRAM = R"( |
| BPF_TABLE("percpu_hash", int, u64, myhash, 128); |
| )"; |
| |
| ebpf::BPF bpf; |
| ebpf::StatusTuple res(0); |
| res = bpf.init(BPF_PROGRAM); |
| REQUIRE(res.code() == 0); |
| |
| ebpf::BPFTable t = bpf.get_table("myhash"); |
| size_t ncpus = ebpf::BPFTable::get_possible_cpu_count(); |
| |
| std::vector<std::string> v1(ncpus); |
| for (size_t i = 0; i < ncpus; i++) { |
| v1.at(i) = std::to_string(42 * i); |
| } |
| |
| // update element |
| std::vector<std::string> value; |
| res = t.update_value("0x07", v1); |
| REQUIRE(res.code() == 0); |
| res = t.get_value("0x07", value); |
| REQUIRE(res.code() == 0); |
| 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.code() == 0); |
| |
| auto t = bpf.get_hash_table<int, int>("myhash"); |
| |
| int key, value; |
| |
| // updaate element |
| key = 0x08; |
| value = 0x43; |
| res = t.update_value(key, value); |
| REQUIRE(res.code() == 0); |
| REQUIRE(t[key] == value); |
| |
| // update another element |
| key = 0x12; |
| value = 0x778; |
| res = t.update_value(key, value); |
| REQUIRE(res.code() == 0); |
| key = 0x31; |
| value = 0x123; |
| res = t.update_value(key, value); |
| REQUIRE(res.code() == 0); |
| key = 0x12; |
| value = 0; |
| res = t.get_value(key, value); |
| REQUIRE(res.code() == 0); |
| REQUIRE(value == 0x778); |
| |
| // remove value and dump table |
| key = 0x12; |
| res = t.remove_value(key); |
| REQUIRE(res.code() == 0); |
| auto values = t.get_table_offline(); |
| REQUIRE(values.size() == 2); |
| |
| // clear table |
| res = t.clear_table_non_atomic(); |
| REQUIRE(res.code() == 0); |
| 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_init(&zero, &stack_id); |
| (*val) = stack_id; |
| |
| return 0; |
| } |
| )"; |
| |
| ebpf::BPF bpf; |
| ebpf::StatusTuple res(0); |
| res = bpf.init(BPF_PROGRAM); |
| REQUIRE(res.code() == 0); |
| std::string getuid_fnname = bpf.get_syscall_fnname("getuid"); |
| res = bpf.attach_kprobe(getuid_fnname, "on_sys_getuid"); |
| REQUIRE(res.code() == 0); |
| REQUIRE(getuid() >= 0); |
| res = bpf.detach_kprobe(getuid_fnname); |
| REQUIRE(res.code() == 0); |
| |
| auto id = bpf.get_hash_table<int, int>("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 |
| } |