|
2 | 2 | // cppcheck-suppress-file missingInclude |
3 | 3 | // cppcheck-suppress-file unknownMacro |
4 | 4 | #include <benchmark/benchmark.h> |
| 5 | +#include <arpa/inet.h> |
| 6 | +#include <cstring> |
5 | 7 | #include <fstream> |
6 | 8 | #include <filesystem> |
| 9 | +#include <sys/sysmacros.h> |
| 10 | +#include "network_ops.hpp" |
7 | 11 | #include "policy.hpp" |
8 | 12 | #include "sha256.hpp" |
9 | 13 | #include "utils.hpp" |
@@ -105,6 +109,312 @@ static void BM_ParseInodeId(benchmark::State& state) |
105 | 109 | } |
106 | 110 | BENCHMARK(BM_ParseInodeId); |
107 | 111 |
|
| 112 | +// ============================================================================ |
| 113 | +// BPF-Specific Benchmarks |
| 114 | +// These test userspace hot paths that interact with BPF maps |
| 115 | +// ============================================================================ |
| 116 | + |
| 117 | +// InodeIdHash - Critical for deny_inode_map lookups |
| 118 | +static void BM_InodeIdHash(benchmark::State& state) |
| 119 | +{ |
| 120 | + InodeIdHash hasher; |
| 121 | + InodeId id{.ino = 12345678901234ULL, .dev = 259, .pad = 0}; |
| 122 | + for (auto _ : state) { |
| 123 | + size_t hash = hasher(id); |
| 124 | + benchmark::DoNotOptimize(hash); |
| 125 | + } |
| 126 | +} |
| 127 | +BENCHMARK(BM_InodeIdHash); |
| 128 | + |
| 129 | +// InodeIdHash with varying inodes (simulates map iteration) |
| 130 | +static void BM_InodeIdHashVarying(benchmark::State& state) |
| 131 | +{ |
| 132 | + InodeIdHash hasher; |
| 133 | + uint64_t ino = 1000000; |
| 134 | + for (auto _ : state) { |
| 135 | + InodeId id{.ino = ino++, .dev = 259, .pad = 0}; |
| 136 | + size_t hash = hasher(id); |
| 137 | + benchmark::DoNotOptimize(hash); |
| 138 | + } |
| 139 | + state.SetItemsProcessed(state.iterations()); |
| 140 | +} |
| 141 | +BENCHMARK(BM_InodeIdHashVarying); |
| 142 | + |
| 143 | +// fill_path_key - Used when adding paths to deny_path_map |
| 144 | +static void BM_FillPathKeyShort(benchmark::State& state) |
| 145 | +{ |
| 146 | + std::string path = "/usr/bin/malware"; |
| 147 | + PathKey key{}; |
| 148 | + for (auto _ : state) { |
| 149 | + fill_path_key(path, key); |
| 150 | + benchmark::DoNotOptimize(key); |
| 151 | + } |
| 152 | + state.SetBytesProcessed(state.iterations() * path.size()); |
| 153 | +} |
| 154 | +BENCHMARK(BM_FillPathKeyShort); |
| 155 | + |
| 156 | +static void BM_FillPathKeyLong(benchmark::State& state) |
| 157 | +{ |
| 158 | + // Near-maximum length path |
| 159 | + std::string path = "/var/lib/containers/storage/overlay/abcdef1234567890/" + |
| 160 | + std::string(150, 'x') + "/binary"; |
| 161 | + PathKey key{}; |
| 162 | + for (auto _ : state) { |
| 163 | + fill_path_key(path, key); |
| 164 | + benchmark::DoNotOptimize(key); |
| 165 | + } |
| 166 | + state.SetBytesProcessed(state.iterations() * path.size()); |
| 167 | +} |
| 168 | +BENCHMARK(BM_FillPathKeyLong); |
| 169 | + |
| 170 | +// encode_dev - Converts dev_t to BPF-compatible format |
| 171 | +static void BM_EncodeDev(benchmark::State& state) |
| 172 | +{ |
| 173 | + dev_t dev = makedev(259, 1); // Typical NVMe device |
| 174 | + for (auto _ : state) { |
| 175 | + uint32_t encoded = encode_dev(dev); |
| 176 | + benchmark::DoNotOptimize(encoded); |
| 177 | + } |
| 178 | +} |
| 179 | +BENCHMARK(BM_EncodeDev); |
| 180 | + |
| 181 | +// DenyEntries map operations (unordered_map with InodeId keys) |
| 182 | +static void BM_DenyEntriesInsert(benchmark::State& state) |
| 183 | +{ |
| 184 | + const size_t map_size = state.range(0); |
| 185 | + for (auto _ : state) { |
| 186 | + state.PauseTiming(); |
| 187 | + DenyEntries entries; |
| 188 | + entries.reserve(map_size); |
| 189 | + state.ResumeTiming(); |
| 190 | + |
| 191 | + for (size_t i = 0; i < map_size; ++i) { |
| 192 | + InodeId id{.ino = i + 1000000, .dev = 259, .pad = 0}; |
| 193 | + entries.emplace(id, "/path/to/file" + std::to_string(i)); |
| 194 | + } |
| 195 | + benchmark::DoNotOptimize(entries); |
| 196 | + } |
| 197 | + state.SetItemsProcessed(state.iterations() * map_size); |
| 198 | +} |
| 199 | +BENCHMARK(BM_DenyEntriesInsert)->Range(100, 10000); |
| 200 | + |
| 201 | +static void BM_DenyEntriesLookup(benchmark::State& state) |
| 202 | +{ |
| 203 | + const size_t map_size = state.range(0); |
| 204 | + DenyEntries entries; |
| 205 | + entries.reserve(map_size); |
| 206 | + for (size_t i = 0; i < map_size; ++i) { |
| 207 | + InodeId id{.ino = i + 1000000, .dev = 259, .pad = 0}; |
| 208 | + entries.emplace(id, "/path/to/file" + std::to_string(i)); |
| 209 | + } |
| 210 | + |
| 211 | + size_t lookup_idx = 0; |
| 212 | + for (auto _ : state) { |
| 213 | + InodeId id{.ino = (lookup_idx % map_size) + 1000000, .dev = 259, .pad = 0}; |
| 214 | + auto it = entries.find(id); |
| 215 | + benchmark::DoNotOptimize(it); |
| 216 | + ++lookup_idx; |
| 217 | + } |
| 218 | +} |
| 219 | +BENCHMARK(BM_DenyEntriesLookup)->Range(100, 10000); |
| 220 | + |
| 221 | +// PortKey hash - For deny_port map lookups |
| 222 | +static void BM_PortKeyHash(benchmark::State& state) |
| 223 | +{ |
| 224 | + PortKeyHash hasher; |
| 225 | + PortKey key{.port = 443, .protocol = 6, .direction = 0}; |
| 226 | + for (auto _ : state) { |
| 227 | + size_t hash = hasher(key); |
| 228 | + benchmark::DoNotOptimize(hash); |
| 229 | + } |
| 230 | +} |
| 231 | +BENCHMARK(BM_PortKeyHash); |
| 232 | + |
| 233 | +// IPv4 parsing - Hot path for network deny rules |
| 234 | +static void BM_ParseIpv4(benchmark::State& state) |
| 235 | +{ |
| 236 | + std::string ip = "192.168.1.100"; |
| 237 | + for (auto _ : state) { |
| 238 | + uint32_t ip_be; |
| 239 | + bool result = parse_ipv4(ip, ip_be); |
| 240 | + benchmark::DoNotOptimize(result); |
| 241 | + benchmark::DoNotOptimize(ip_be); |
| 242 | + } |
| 243 | +} |
| 244 | +BENCHMARK(BM_ParseIpv4); |
| 245 | + |
| 246 | +// IPv6 parsing - For IPv6 deny rules |
| 247 | +static void BM_ParseIpv6(benchmark::State& state) |
| 248 | +{ |
| 249 | + std::string ip = "2001:db8::8a2e:370:7334"; |
| 250 | + for (auto _ : state) { |
| 251 | + Ipv6Key key{}; |
| 252 | + bool result = parse_ipv6(ip, key); |
| 253 | + benchmark::DoNotOptimize(result); |
| 254 | + benchmark::DoNotOptimize(key); |
| 255 | + } |
| 256 | +} |
| 257 | +BENCHMARK(BM_ParseIpv6); |
| 258 | + |
| 259 | +// IPv6 parsing - Full address |
| 260 | +static void BM_ParseIpv6Full(benchmark::State& state) |
| 261 | +{ |
| 262 | + std::string ip = "2001:0db8:85a3:0000:0000:8a2e:0370:7334"; |
| 263 | + for (auto _ : state) { |
| 264 | + Ipv6Key key{}; |
| 265 | + bool result = parse_ipv6(ip, key); |
| 266 | + benchmark::DoNotOptimize(result); |
| 267 | + benchmark::DoNotOptimize(key); |
| 268 | + } |
| 269 | +} |
| 270 | +BENCHMARK(BM_ParseIpv6Full); |
| 271 | + |
| 272 | +// CIDR v4 parsing - For LPM trie operations |
| 273 | +static void BM_ParseCidrV4(benchmark::State& state) |
| 274 | +{ |
| 275 | + std::string cidr = "10.0.0.0/8"; |
| 276 | + for (auto _ : state) { |
| 277 | + uint32_t ip_be; |
| 278 | + uint8_t prefix_len; |
| 279 | + bool result = parse_cidr_v4(cidr, ip_be, prefix_len); |
| 280 | + benchmark::DoNotOptimize(result); |
| 281 | + benchmark::DoNotOptimize(ip_be); |
| 282 | + benchmark::DoNotOptimize(prefix_len); |
| 283 | + } |
| 284 | +} |
| 285 | +BENCHMARK(BM_ParseCidrV4); |
| 286 | + |
| 287 | +// CIDR v6 parsing |
| 288 | +static void BM_ParseCidrV6(benchmark::State& state) |
| 289 | +{ |
| 290 | + std::string cidr = "2001:db8::/32"; |
| 291 | + for (auto _ : state) { |
| 292 | + Ipv6Key ip{}; |
| 293 | + uint8_t prefix_len; |
| 294 | + bool result = parse_cidr_v6(cidr, ip, prefix_len); |
| 295 | + benchmark::DoNotOptimize(result); |
| 296 | + benchmark::DoNotOptimize(ip); |
| 297 | + benchmark::DoNotOptimize(prefix_len); |
| 298 | + } |
| 299 | +} |
| 300 | +BENCHMARK(BM_ParseCidrV6); |
| 301 | + |
| 302 | +// LPM key construction - IPv4 |
| 303 | +static void BM_Ipv4LpmKeyConstruction(benchmark::State& state) |
| 304 | +{ |
| 305 | + uint32_t ip_be = htonl(0x0A000000); // 10.0.0.0 |
| 306 | + uint8_t prefix_len = 8; |
| 307 | + for (auto _ : state) { |
| 308 | + Ipv4LpmKey key = { |
| 309 | + .prefixlen = prefix_len, |
| 310 | + .addr = ip_be}; |
| 311 | + benchmark::DoNotOptimize(key); |
| 312 | + } |
| 313 | +} |
| 314 | +BENCHMARK(BM_Ipv4LpmKeyConstruction); |
| 315 | + |
| 316 | +// LPM key construction - IPv6 |
| 317 | +static void BM_Ipv6LpmKeyConstruction(benchmark::State& state) |
| 318 | +{ |
| 319 | + Ipv6Key ip{}; |
| 320 | + parse_ipv6("2001:db8::", ip); |
| 321 | + uint8_t prefix_len = 32; |
| 322 | + for (auto _ : state) { |
| 323 | + Ipv6LpmKey key = { |
| 324 | + .prefixlen = prefix_len, |
| 325 | + .addr = {0}}; |
| 326 | + std::memcpy(key.addr, ip.addr, sizeof(key.addr)); |
| 327 | + benchmark::DoNotOptimize(key); |
| 328 | + } |
| 329 | +} |
| 330 | +BENCHMARK(BM_Ipv6LpmKeyConstruction); |
| 331 | + |
| 332 | +// IP formatting (for logging/output) |
| 333 | +static void BM_FormatIpv4(benchmark::State& state) |
| 334 | +{ |
| 335 | + uint32_t ip_be = htonl(0xC0A80164); // 192.168.1.100 |
| 336 | + for (auto _ : state) { |
| 337 | + std::string result = format_ipv4(ip_be); |
| 338 | + benchmark::DoNotOptimize(result); |
| 339 | + } |
| 340 | +} |
| 341 | +BENCHMARK(BM_FormatIpv4); |
| 342 | + |
| 343 | +static void BM_FormatIpv6(benchmark::State& state) |
| 344 | +{ |
| 345 | + Ipv6Key ip{}; |
| 346 | + parse_ipv6("2001:db8::8a2e:370:7334", ip); |
| 347 | + for (auto _ : state) { |
| 348 | + std::string result = format_ipv6(ip); |
| 349 | + benchmark::DoNotOptimize(result); |
| 350 | + } |
| 351 | +} |
| 352 | +BENCHMARK(BM_FormatIpv6); |
| 353 | + |
| 354 | +// inode_to_string - Used in logging and output |
| 355 | +static void BM_InodeToString(benchmark::State& state) |
| 356 | +{ |
| 357 | + InodeId id{.ino = 12345678901234ULL, .dev = 259, .pad = 0}; |
| 358 | + for (auto _ : state) { |
| 359 | + std::string result = inode_to_string(id); |
| 360 | + benchmark::DoNotOptimize(result); |
| 361 | + } |
| 362 | +} |
| 363 | +BENCHMARK(BM_InodeToString); |
| 364 | + |
| 365 | +// build_exec_id - Used for event correlation |
| 366 | +static void BM_BuildExecId(benchmark::State& state) |
| 367 | +{ |
| 368 | + uint32_t pid = 12345; |
| 369 | + uint64_t start_time = 1234567890123456789ULL; |
| 370 | + for (auto _ : state) { |
| 371 | + std::string result = build_exec_id(pid, start_time); |
| 372 | + benchmark::DoNotOptimize(result); |
| 373 | + } |
| 374 | +} |
| 375 | +BENCHMARK(BM_BuildExecId); |
| 376 | + |
| 377 | +// InodeId comparison (for equality checks in maps) |
| 378 | +static void BM_InodeIdComparison(benchmark::State& state) |
| 379 | +{ |
| 380 | + InodeId id1{.ino = 12345678901234ULL, .dev = 259, .pad = 0}; |
| 381 | + InodeId id2{.ino = 12345678901234ULL, .dev = 259, .pad = 0}; |
| 382 | + InodeId id3{.ino = 12345678901235ULL, .dev = 259, .pad = 0}; |
| 383 | + bool toggle = false; |
| 384 | + for (auto _ : state) { |
| 385 | + bool result = toggle ? (id1 == id2) : (id1 == id3); |
| 386 | + benchmark::DoNotOptimize(result); |
| 387 | + toggle = !toggle; |
| 388 | + } |
| 389 | +} |
| 390 | +BENCHMARK(BM_InodeIdComparison); |
| 391 | + |
| 392 | +// Protocol name lookup (for output formatting) |
| 393 | +static void BM_ProtocolName(benchmark::State& state) |
| 394 | +{ |
| 395 | + uint8_t protocols[] = {0, 6, 17, 99}; |
| 396 | + size_t idx = 0; |
| 397 | + for (auto _ : state) { |
| 398 | + std::string name = protocol_name(protocols[idx % 4]); |
| 399 | + benchmark::DoNotOptimize(name); |
| 400 | + ++idx; |
| 401 | + } |
| 402 | +} |
| 403 | +BENCHMARK(BM_ProtocolName); |
| 404 | + |
| 405 | +// Direction name lookup |
| 406 | +static void BM_DirectionName(benchmark::State& state) |
| 407 | +{ |
| 408 | + uint8_t directions[] = {0, 1, 2, 255}; |
| 409 | + size_t idx = 0; |
| 410 | + for (auto _ : state) { |
| 411 | + std::string name = direction_name(directions[idx % 4]); |
| 412 | + benchmark::DoNotOptimize(name); |
| 413 | + ++idx; |
| 414 | + } |
| 415 | +} |
| 416 | +BENCHMARK(BM_DirectionName); |
| 417 | + |
108 | 418 | } // namespace |
109 | 419 | } // namespace aegis |
110 | 420 |
|
|
0 commit comments