1 /* 2 * Copyright (c) 2017 Politecnico di Torino 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include <linux/version.h> 18 #include <unistd.h> 19 #include <string> 20 21 #include "BPF.h" 22 #include "catch.hpp" 23 24 TEST_CASE("test bpf table", "[bpf_table]") { 25 const std::string BPF_PROGRAM = R"( 26 BPF_TABLE("hash", int, int, myhash, 128); 27 )"; 28 29 ebpf::BPF *bpf(new ebpf::BPF); 30 ebpf::StatusTuple res(0); 31 std::vector<std::pair<std::string, std::string>> elements; 32 res = bpf->init(BPF_PROGRAM); 33 REQUIRE(res.code() == 0); 34 35 ebpf::BPFTable t = bpf->get_table("myhash"); 36 37 // update element 38 std::string value; 39 res = t.update_value("0x07", "0x42"); 40 REQUIRE(res.code() == 0); 41 res = t.get_value("0x7", value); 42 REQUIRE(res.code() == 0); 43 REQUIRE(value == "0x42"); 44 45 // update another element 46 res = t.update_value("0x11", "0x777"); 47 REQUIRE(res.code() == 0); 48 res = t.get_value("0x11", value); 49 REQUIRE(res.code() == 0); 50 REQUIRE(value == "0x777"); 51 52 // remove value 53 res = t.remove_value("0x11"); 54 REQUIRE(res.code() == 0); 55 res = t.get_value("0x11", value); 56 REQUIRE(res.code() != 0); 57 58 res = t.update_value("0x15", "0x888"); 59 REQUIRE(res.code() == 0); 60 res = t.get_table_offline(elements); 61 REQUIRE(res.code() == 0); 62 REQUIRE(elements.size() == 2); 63 64 // check that elements match what is in the table 65 for (auto &it : elements) { 66 if (it.first == "0x15") { 67 REQUIRE(it.second == "0x888"); 68 } else if (it.first == "0x7") { 69 REQUIRE(it.second == "0x42"); 70 } else { 71 FAIL("Element " + it.first + " should not be on the table", it.first); 72 } 73 } 74 75 res = t.clear_table_non_atomic(); 76 REQUIRE(res.code() == 0); 77 res = t.get_table_offline(elements); 78 REQUIRE(res.code() == 0); 79 REQUIRE(elements.size() == 0); 80 81 // delete bpf_module, call to key/leaf printf/scanf must fail 82 delete bpf; 83 84 res = t.update_value("0x07", "0x42"); 85 REQUIRE(res.code() != 0); 86 87 res = t.get_value("0x07", value); 88 REQUIRE(res.code() != 0); 89 90 res = t.remove_value("0x07"); 91 REQUIRE(res.code() != 0); 92 } 93 94 #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 6, 0) 95 TEST_CASE("test bpf percpu tables", "[bpf_percpu_table]") { 96 const std::string BPF_PROGRAM = R"( 97 BPF_TABLE("percpu_hash", int, u64, myhash, 128); 98 )"; 99 100 ebpf::BPF bpf; 101 ebpf::StatusTuple res(0); 102 res = bpf.init(BPF_PROGRAM); 103 REQUIRE(res.code() == 0); 104 105 ebpf::BPFTable t = bpf.get_table("myhash"); 106 size_t ncpus = ebpf::BPFTable::get_possible_cpu_count(); 107 108 std::vector<std::string> v1(ncpus); 109 for (size_t i = 0; i < ncpus; i++) { 110 v1.at(i) = std::to_string(42 * i); 111 } 112 113 // update element 114 std::vector<std::string> value; 115 res = t.update_value("0x07", v1); 116 REQUIRE(res.code() == 0); 117 res = t.get_value("0x07", value); 118 REQUIRE(res.code() == 0); 119 for (size_t i = 0; i < ncpus; i++) { 120 REQUIRE(42 * i == std::stoul(value.at(i), nullptr, 16)); 121 } 122 } 123 #endif 124 125 TEST_CASE("test bpf hash table", "[bpf_hash_table]") { 126 const std::string BPF_PROGRAM = R"( 127 BPF_HASH(myhash, int, int, 128); 128 )"; 129 130 ebpf::BPF bpf; 131 ebpf::StatusTuple res(0); 132 res = bpf.init(BPF_PROGRAM); 133 REQUIRE(res.code() == 0); 134 135 auto t = bpf.get_hash_table<int, int>("myhash"); 136 137 int key, value; 138 139 // updaate element 140 key = 0x08; 141 value = 0x43; 142 res = t.update_value(key, value); 143 REQUIRE(res.code() == 0); 144 REQUIRE(t[key] == value); 145 146 // update another element 147 key = 0x12; 148 value = 0x778; 149 res = t.update_value(key, value); 150 REQUIRE(res.code() == 0); 151 key = 0x31; 152 value = 0x123; 153 res = t.update_value(key, value); 154 REQUIRE(res.code() == 0); 155 key = 0x12; 156 value = 0; 157 res = t.get_value(key, value); 158 REQUIRE(res.code() == 0); 159 REQUIRE(value == 0x778); 160 161 // remove value and dump table 162 key = 0x12; 163 res = t.remove_value(key); 164 REQUIRE(res.code() == 0); 165 auto values = t.get_table_offline(); 166 REQUIRE(values.size() == 2); 167 168 // clear table 169 res = t.clear_table_non_atomic(); 170 REQUIRE(res.code() == 0); 171 values = t.get_table_offline(); 172 REQUIRE(values.size() == 0); 173 } 174 175 TEST_CASE("test bpf stack table", "[bpf_stack_table]") { 176 #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 6, 0) 177 const std::string BPF_PROGRAM = R"( 178 BPF_HASH(id, int, int, 1); 179 BPF_STACK_TRACE(stack_traces, 8); 180 181 int on_sys_getuid(void *ctx) { 182 int stack_id = stack_traces.get_stackid(ctx, BPF_F_REUSE_STACKID); 183 int zero = 0, *val; 184 val = id.lookup_or_init(&zero, &stack_id); 185 (*val) = stack_id; 186 187 return 0; 188 } 189 )"; 190 191 ebpf::BPF bpf; 192 ebpf::StatusTuple res(0); 193 res = bpf.init(BPF_PROGRAM); 194 REQUIRE(res.code() == 0); 195 std::string getuid_fnname = bpf.get_syscall_fnname("getuid"); 196 res = bpf.attach_kprobe(getuid_fnname, "on_sys_getuid"); 197 REQUIRE(res.code() == 0); 198 REQUIRE(getuid() >= 0); 199 res = bpf.detach_kprobe(getuid_fnname); 200 REQUIRE(res.code() == 0); 201 202 auto id = bpf.get_hash_table<int, int>("id"); 203 auto stack_traces = bpf.get_stack_table("stack_traces"); 204 205 int stack_id = id[0]; 206 REQUIRE(stack_id >= 0); 207 208 auto addrs = stack_traces.get_stack_addr(stack_id); 209 auto symbols = stack_traces.get_stack_symbol(stack_id, -1); 210 REQUIRE(addrs.size() > 0); 211 REQUIRE(addrs.size() == symbols.size()); 212 bool found = false; 213 for (const auto &symbol : symbols) 214 if (symbol.find("sys_getuid") != std::string::npos) { 215 found = true; 216 break; 217 } 218 REQUIRE(found); 219 220 stack_traces.clear_table_non_atomic(); 221 addrs = stack_traces.get_stack_addr(stack_id); 222 REQUIRE(addrs.size() == 0); 223 #endif 224 } 225