| 1 | |
| 2 | |
| 3 | * Testsuite for eBPF maps
|
| 4 | *
|
| 5 | * Copyright (c) 2014 PLUMgrid, http://plumgrid.com
|
| 6 | * Copyright (c) 2016 Facebook
|
| 7 |
|
| 8 |
|
| 9 | #include <stdio.h>
|
| 10 | #include <unistd.h>
|
| 11 | #include <errno.h>
|
| 12 | #include <string.h>
|
| 13 | #include <assert.h>
|
| 14 | #include <stdlib.h>
|
| 15 | #include <time.h>
|
| 16 |
|
| 17 | #include <sys/wait.h>
|
| 18 | #include <sys/socket.h>
|
| 19 | #include <netinet/in.h>
|
| 20 | #include <linux/bpf.h>
|
| 21 |
|
| 22 | #include <bpf/bpf.h>
|
| 23 | #include <bpf/libbpf.h>
|
| 24 |
|
| 25 | #include "bpf_util.h"
|
| 26 | #include "test_maps.h"
|
| 27 | #include "testing_helpers.h"
|
| 28 |
|
| 29 | int skips;
|
| 30 |
|
| 31 | static struct bpf_map_create_opts map_opts = { .sz = sizeof(map_opts) };
|
| 32 |
|
| 33 | static void test_hashmap(unsigned int task, void *data)
|
| 34 | {
|
| 35 | long long key, next_key, first_key, value;
|
| 36 | int fd;
|
| 37 |
|
| 38 | fd = bpf_map_create(BPF_MAP_TYPE_HASH, NULL, sizeof(key), sizeof(value), 2, &map_opts);
|
| 39 | if (fd < 0) {
|
| 40 | printf("Failed to create hashmap '%s'!\n", strerror(errno));
|
| 41 | exit(1);
|
| 42 | }
|
| 43 |
|
| 44 | key = 1;
|
| 45 | value = 1234;
|
| 46 |
|
| 47 | assert(bpf_map_update_elem(fd, &key, &value, BPF_ANY) == 0);
|
| 48 |
|
| 49 | value = 0;
|
| 50 |
|
| 51 | assert(bpf_map_update_elem(fd, &key, &value, BPF_NOEXIST) < 0 &&
|
| 52 |
|
| 53 | errno == EEXIST);
|
| 54 |
|
| 55 |
|
| 56 | assert(bpf_map_update_elem(fd, &key, &value, -1) < 0 &&
|
| 57 | errno == EINVAL);
|
| 58 |
|
| 59 |
|
| 60 | assert(bpf_map_lookup_elem(fd, &key, &value) == 0 && value == 1234);
|
| 61 |
|
| 62 | key = 2;
|
| 63 | value = 1234;
|
| 64 |
|
| 65 | assert(bpf_map_update_elem(fd, &key, &value, BPF_ANY) == 0);
|
| 66 |
|
| 67 |
|
| 68 | assert(bpf_map_lookup_and_delete_elem(fd, &key, &value) == 0 && value == 1234);
|
| 69 |
|
| 70 |
|
| 71 | assert(bpf_map_lookup_elem(fd, &key, &value) < 0 && errno == ENOENT);
|
| 72 |
|
| 73 |
|
| 74 | assert(bpf_map_update_elem(fd, &key, &value, BPF_EXIST) < 0 &&
|
| 75 |
|
| 76 | errno == ENOENT);
|
| 77 |
|
| 78 |
|
| 79 | assert(bpf_map_update_elem(fd, &key, &value, BPF_NOEXIST) == 0);
|
| 80 |
|
| 81 | |
| 82 | * inserted due to max_entries limit.
|
| 83 |
|
| 84 | key = 0;
|
| 85 | assert(bpf_map_update_elem(fd, &key, &value, BPF_NOEXIST) < 0 &&
|
| 86 | errno == E2BIG);
|
| 87 |
|
| 88 |
|
| 89 | key = 1;
|
| 90 | assert(bpf_map_update_elem(fd, &key, &value, BPF_EXIST) == 0);
|
| 91 | key = 2;
|
| 92 | assert(bpf_map_update_elem(fd, &key, &value, BPF_ANY) == 0);
|
| 93 | key = 3;
|
| 94 | assert(bpf_map_update_elem(fd, &key, &value, BPF_NOEXIST) < 0 &&
|
| 95 | errno == E2BIG);
|
| 96 |
|
| 97 |
|
| 98 | key = 0;
|
| 99 | assert(bpf_map_delete_elem(fd, &key) < 0 && errno == ENOENT);
|
| 100 |
|
| 101 |
|
| 102 | assert(bpf_map_get_next_key(fd, NULL, &first_key) == 0 &&
|
| 103 | (first_key == 1 || first_key == 2));
|
| 104 | assert(bpf_map_get_next_key(fd, &key, &next_key) == 0 &&
|
| 105 | (next_key == first_key));
|
| 106 | assert(bpf_map_get_next_key(fd, &next_key, &next_key) == 0 &&
|
| 107 | (next_key == 1 || next_key == 2) &&
|
| 108 | (next_key != first_key));
|
| 109 | assert(bpf_map_get_next_key(fd, &next_key, &next_key) < 0 &&
|
| 110 | errno == ENOENT);
|
| 111 |
|
| 112 |
|
| 113 | key = 1;
|
| 114 | assert(bpf_map_delete_elem(fd, &key) == 0);
|
| 115 | key = 2;
|
| 116 | assert(bpf_map_delete_elem(fd, &key) == 0);
|
| 117 | assert(bpf_map_delete_elem(fd, &key) < 0 && errno == ENOENT);
|
| 118 |
|
| 119 | key = 0;
|
| 120 |
|
| 121 | assert(bpf_map_get_next_key(fd, NULL, &next_key) < 0 &&
|
| 122 | errno == ENOENT);
|
| 123 | assert(bpf_map_get_next_key(fd, &key, &next_key) < 0 &&
|
| 124 | errno == ENOENT);
|
| 125 |
|
| 126 | close(fd);
|
| 127 | }
|
| 128 |
|
| 129 | static void test_hashmap_sizes(unsigned int task, void *data)
|
| 130 | {
|
| 131 | int fd, i, j;
|
| 132 |
|
| 133 | for (i = 1; i <= 512; i <<= 1)
|
| 134 | for (j = 1; j <= 1 << 18; j <<= 1) {
|
| 135 | fd = bpf_map_create(BPF_MAP_TYPE_HASH, NULL, i, j, 2, &map_opts);
|
| 136 | if (fd < 0) {
|
| 137 | if (errno == ENOMEM)
|
| 138 | return;
|
| 139 | printf("Failed to create hashmap key=%d value=%d '%s'\n",
|
| 140 | i, j, strerror(errno));
|
| 141 | exit(1);
|
| 142 | }
|
| 143 | close(fd);
|
| 144 | usleep(10);
|
| 145 | }
|
| 146 | }
|
| 147 |
|
| 148 | static void test_hashmap_percpu(unsigned int task, void *data)
|
| 149 | {
|
| 150 | unsigned int nr_cpus = bpf_num_possible_cpus();
|
| 151 | BPF_DECLARE_PERCPU(long, value);
|
| 152 | long long key, next_key, first_key;
|
| 153 | int expected_key_mask = 0;
|
| 154 | int fd, i;
|
| 155 |
|
| 156 | fd = bpf_map_create(BPF_MAP_TYPE_PERCPU_HASH, NULL, sizeof(key),
|
| 157 | sizeof(bpf_percpu(value, 0)), 2, &map_opts);
|
| 158 | if (fd < 0) {
|
| 159 | printf("Failed to create hashmap '%s'!\n", strerror(errno));
|
| 160 | exit(1);
|
| 161 | }
|
| 162 |
|
| 163 | for (i = 0; i < nr_cpus; i++)
|
| 164 | bpf_percpu(value, i) = i + 100;
|
| 165 |
|
| 166 | key = 1;
|
| 167 |
|
| 168 | assert(!(expected_key_mask & key));
|
| 169 | assert(bpf_map_update_elem(fd, &key, value, BPF_ANY) == 0);
|
| 170 |
|
| 171 |
|
| 172 | assert(bpf_map_lookup_and_delete_elem(fd, &key, value) == 0 &&
|
| 173 | bpf_percpu(value,0) == 100);
|
| 174 |
|
| 175 | for (i = 0; i < nr_cpus; i++)
|
| 176 | bpf_percpu(value,i) = i + 100;
|
| 177 |
|
| 178 |
|
| 179 | assert(bpf_map_update_elem(fd, &key, value, BPF_NOEXIST) == 0);
|
| 180 | expected_key_mask |= key;
|
| 181 |
|
| 182 |
|
| 183 | assert(bpf_map_update_elem(fd, &key, value, BPF_NOEXIST) < 0 &&
|
| 184 |
|
| 185 | errno == EEXIST);
|
| 186 |
|
| 187 |
|
| 188 | assert(bpf_map_update_elem(fd, &key, value, -1) < 0 &&
|
| 189 | errno == EINVAL);
|
| 190 |
|
| 191 | |
| 192 | * was run from a different CPU.
|
| 193 |
|
| 194 | bpf_percpu(value, 0) = 1;
|
| 195 | assert(bpf_map_lookup_elem(fd, &key, value) == 0 &&
|
| 196 | bpf_percpu(value, 0) == 100);
|
| 197 |
|
| 198 | key = 2;
|
| 199 |
|
| 200 | assert(bpf_map_lookup_elem(fd, &key, value) < 0 && errno == ENOENT);
|
| 201 |
|
| 202 |
|
| 203 | assert(bpf_map_update_elem(fd, &key, value, BPF_EXIST) < 0 &&
|
| 204 |
|
| 205 | errno == ENOENT);
|
| 206 |
|
| 207 |
|
| 208 | assert(!(expected_key_mask & key));
|
| 209 | assert(bpf_map_update_elem(fd, &key, value, BPF_NOEXIST) == 0);
|
| 210 | expected_key_mask |= key;
|
| 211 |
|
| 212 | |
| 213 | * inserted due to max_entries limit.
|
| 214 |
|
| 215 | key = 0;
|
| 216 | assert(bpf_map_update_elem(fd, &key, value, BPF_NOEXIST) < 0 &&
|
| 217 | errno == E2BIG);
|
| 218 |
|
| 219 |
|
| 220 | assert(bpf_map_delete_elem(fd, &key) < 0 && errno == ENOENT);
|
| 221 |
|
| 222 |
|
| 223 | assert(bpf_map_get_next_key(fd, NULL, &first_key) == 0 &&
|
| 224 | ((expected_key_mask & first_key) == first_key));
|
| 225 | while (!bpf_map_get_next_key(fd, &key, &next_key)) {
|
| 226 | if (first_key) {
|
| 227 | assert(next_key == first_key);
|
| 228 | first_key = 0;
|
| 229 | }
|
| 230 | assert((expected_key_mask & next_key) == next_key);
|
| 231 | expected_key_mask &= ~next_key;
|
| 232 |
|
| 233 | assert(bpf_map_lookup_elem(fd, &next_key, value) == 0);
|
| 234 |
|
| 235 | for (i = 0; i < nr_cpus; i++)
|
| 236 | assert(bpf_percpu(value, i) == i + 100);
|
| 237 |
|
| 238 | key = next_key;
|
| 239 | }
|
| 240 | assert(errno == ENOENT);
|
| 241 |
|
| 242 |
|
| 243 | key = 1;
|
| 244 | assert(bpf_map_update_elem(fd, &key, value, BPF_EXIST) == 0);
|
| 245 |
|
| 246 |
|
| 247 | key = 1;
|
| 248 | assert(bpf_map_delete_elem(fd, &key) == 0);
|
| 249 | key = 2;
|
| 250 | assert(bpf_map_delete_elem(fd, &key) == 0);
|
| 251 | assert(bpf_map_delete_elem(fd, &key) < 0 && errno == ENOENT);
|
| 252 |
|
| 253 | key = 0;
|
| 254 |
|
| 255 | assert(bpf_map_get_next_key(fd, NULL, &next_key) < 0 &&
|
| 256 | errno == ENOENT);
|
| 257 | assert(bpf_map_get_next_key(fd, &key, &next_key) < 0 &&
|
| 258 | errno == ENOENT);
|
| 259 |
|
| 260 | close(fd);
|
| 261 | }
|
| 262 |
|
| 263 | #define MAP_RETRIES 20
|
| 264 |
|
| 265 | static bool can_retry(int err)
|
| 266 | {
|
| 267 | return (err == EAGAIN || err == EBUSY ||
|
| 268 | ((err == ENOMEM || err == E2BIG) &&
|
| 269 | map_opts.map_flags == BPF_F_NO_PREALLOC));
|
| 270 | }
|
| 271 |
|
| 272 |
|
| 273 | #define VALUE_SIZE 3
|
| 274 | static int helper_fill_hashmap(int max_entries)
|
| 275 | {
|
| 276 | int i, fd, ret;
|
| 277 | long long key, value[VALUE_SIZE] = {};
|
| 278 |
|
| 279 | fd = bpf_map_create(BPF_MAP_TYPE_HASH, NULL, sizeof(key), sizeof(value),
|
| 280 | max_entries, &map_opts);
|
| 281 | CHECK(fd < 0,
|
| 282 | "failed to create hashmap",
|
| 283 | "err: %s, flags: 0x%x\n", strerror(errno), map_opts.map_flags);
|
| 284 |
|
| 285 | for (i = 0; i < max_entries; i++) {
|
| 286 | key = i; value[0] = key;
|
| 287 | ret = map_update_retriable(fd, &key, value, BPF_NOEXIST,
|
| 288 | MAP_RETRIES, can_retry);
|
| 289 | CHECK(ret != 0,
|
| 290 | "can't update hashmap",
|
| 291 | "err: %s\n", strerror(-ret));
|
| 292 | }
|
| 293 |
|
| 294 | return fd;
|
| 295 | }
|
| 296 |
|
| 297 | static void test_hashmap_walk(unsigned int task, void *data)
|
| 298 | {
|
| 299 | int fd, i, max_entries = 10000;
|
| 300 | long long key, value[VALUE_SIZE], next_key;
|
| 301 | bool next_key_valid = true;
|
| 302 |
|
| 303 | fd = helper_fill_hashmap(max_entries);
|
| 304 |
|
| 305 | for (i = 0; bpf_map_get_next_key(fd, !i ? NULL : &key,
|
| 306 | &next_key) == 0; i++) {
|
| 307 | key = next_key;
|
| 308 | assert(bpf_map_lookup_elem(fd, &key, value) == 0);
|
| 309 | }
|
| 310 |
|
| 311 | assert(i == max_entries);
|
| 312 |
|
| 313 | assert(bpf_map_get_next_key(fd, NULL, &key) == 0);
|
| 314 | for (i = 0; next_key_valid; i++) {
|
| 315 | next_key_valid = bpf_map_get_next_key(fd, &key, &next_key) == 0;
|
| 316 | assert(bpf_map_lookup_elem(fd, &key, value) == 0);
|
| 317 | value[0]++;
|
| 318 | assert(bpf_map_update_elem(fd, &key, value, BPF_EXIST) == 0);
|
| 319 | key = next_key;
|
| 320 | }
|
| 321 |
|
| 322 | assert(i == max_entries);
|
| 323 |
|
| 324 | for (i = 0; bpf_map_get_next_key(fd, !i ? NULL : &key,
|
| 325 | &next_key) == 0; i++) {
|
| 326 | key = next_key;
|
| 327 | assert(bpf_map_lookup_elem(fd, &key, value) == 0);
|
| 328 | assert(value[0] - 1 == key);
|
| 329 | }
|
| 330 |
|
| 331 | assert(i == max_entries);
|
| 332 | close(fd);
|
| 333 | }
|
| 334 |
|
| 335 | static void test_hashmap_zero_seed(void)
|
| 336 | {
|
| 337 | int i, first, second, old_flags;
|
| 338 | long long key, next_first, next_second;
|
| 339 |
|
| 340 | old_flags = map_opts.map_flags;
|
| 341 | map_opts.map_flags |= BPF_F_ZERO_SEED;
|
| 342 |
|
| 343 | first = helper_fill_hashmap(3);
|
| 344 | second = helper_fill_hashmap(3);
|
| 345 |
|
| 346 | for (i = 0; ; i++) {
|
| 347 | void *key_ptr = !i ? NULL : &key;
|
| 348 |
|
| 349 | if (bpf_map_get_next_key(first, key_ptr, &next_first) != 0)
|
| 350 | break;
|
| 351 |
|
| 352 | CHECK(bpf_map_get_next_key(second, key_ptr, &next_second) != 0,
|
| 353 | "next_key for second map must succeed",
|
| 354 | "key_ptr: %p", key_ptr);
|
| 355 | CHECK(next_first != next_second,
|
| 356 | "keys must match",
|
| 357 | "i: %d first: %lld second: %lld\n", i,
|
| 358 | next_first, next_second);
|
| 359 |
|
| 360 | key = next_first;
|
| 361 | }
|
| 362 |
|
| 363 | map_opts.map_flags = old_flags;
|
| 364 | close(first);
|
| 365 | close(second);
|
| 366 | }
|
| 367 |
|
| 368 | static void test_arraymap(unsigned int task, void *data)
|
| 369 | {
|
| 370 | int key, next_key, fd;
|
| 371 | long long value;
|
| 372 |
|
| 373 | fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, NULL, sizeof(key), sizeof(value), 2, NULL);
|
| 374 | if (fd < 0) {
|
| 375 | printf("Failed to create arraymap '%s'!\n", strerror(errno));
|
| 376 | exit(1);
|
| 377 | }
|
| 378 |
|
| 379 | key = 1;
|
| 380 | value = 1234;
|
| 381 |
|
| 382 | assert(bpf_map_update_elem(fd, &key, &value, BPF_ANY) == 0);
|
| 383 |
|
| 384 | value = 0;
|
| 385 | assert(bpf_map_update_elem(fd, &key, &value, BPF_NOEXIST) < 0 &&
|
| 386 | errno == EEXIST);
|
| 387 |
|
| 388 |
|
| 389 | assert(bpf_map_lookup_elem(fd, &key, &value) == 0 && value == 1234);
|
| 390 |
|
| 391 | key = 0;
|
| 392 |
|
| 393 | assert(bpf_map_lookup_elem(fd, &key, &value) == 0 && value == 0);
|
| 394 |
|
| 395 | |
| 396 | * due to max_entries limit.
|
| 397 |
|
| 398 | key = 2;
|
| 399 | assert(bpf_map_update_elem(fd, &key, &value, BPF_EXIST) < 0 &&
|
| 400 | errno == E2BIG);
|
| 401 |
|
| 402 |
|
| 403 | assert(bpf_map_lookup_elem(fd, &key, &value) < 0 && errno == ENOENT);
|
| 404 |
|
| 405 |
|
| 406 | assert(bpf_map_get_next_key(fd, NULL, &next_key) == 0 &&
|
| 407 | next_key == 0);
|
| 408 | assert(bpf_map_get_next_key(fd, &key, &next_key) == 0 &&
|
| 409 | next_key == 0);
|
| 410 | assert(bpf_map_get_next_key(fd, &next_key, &next_key) == 0 &&
|
| 411 | next_key == 1);
|
| 412 | assert(bpf_map_get_next_key(fd, &next_key, &next_key) < 0 &&
|
| 413 | errno == ENOENT);
|
| 414 |
|
| 415 |
|
| 416 | key = 1;
|
| 417 | assert(bpf_map_delete_elem(fd, &key) < 0 && errno == EINVAL);
|
| 418 |
|
| 419 | close(fd);
|
| 420 | }
|
| 421 |
|
| 422 | static void test_arraymap_percpu(unsigned int task, void *data)
|
| 423 | {
|
| 424 | unsigned int nr_cpus = bpf_num_possible_cpus();
|
| 425 | BPF_DECLARE_PERCPU(long, values);
|
| 426 | int key, next_key, fd, i;
|
| 427 |
|
| 428 | fd = bpf_map_create(BPF_MAP_TYPE_PERCPU_ARRAY, NULL, sizeof(key),
|
| 429 | sizeof(bpf_percpu(values, 0)), 2, NULL);
|
| 430 | if (fd < 0) {
|
| 431 | printf("Failed to create arraymap '%s'!\n", strerror(errno));
|
| 432 | exit(1);
|
| 433 | }
|
| 434 |
|
| 435 | for (i = 0; i < nr_cpus; i++)
|
| 436 | bpf_percpu(values, i) = i + 100;
|
| 437 |
|
| 438 | key = 1;
|
| 439 |
|
| 440 | assert(bpf_map_update_elem(fd, &key, values, BPF_ANY) == 0);
|
| 441 |
|
| 442 | bpf_percpu(values, 0) = 0;
|
| 443 | assert(bpf_map_update_elem(fd, &key, values, BPF_NOEXIST) < 0 &&
|
| 444 | errno == EEXIST);
|
| 445 |
|
| 446 |
|
| 447 | assert(bpf_map_lookup_elem(fd, &key, values) == 0 &&
|
| 448 | bpf_percpu(values, 0) == 100);
|
| 449 |
|
| 450 | key = 0;
|
| 451 |
|
| 452 | assert(bpf_map_lookup_elem(fd, &key, values) == 0 &&
|
| 453 | bpf_percpu(values, 0) == 0 &&
|
| 454 | bpf_percpu(values, nr_cpus - 1) == 0);
|
| 455 |
|
| 456 |
|
| 457 | key = 2;
|
| 458 | assert(bpf_map_update_elem(fd, &key, values, BPF_EXIST) < 0 &&
|
| 459 | errno == E2BIG);
|
| 460 |
|
| 461 |
|
| 462 | assert(bpf_map_lookup_elem(fd, &key, values) < 0 && errno == ENOENT);
|
| 463 |
|
| 464 |
|
| 465 | assert(bpf_map_get_next_key(fd, NULL, &next_key) == 0 &&
|
| 466 | next_key == 0);
|
| 467 | assert(bpf_map_get_next_key(fd, &key, &next_key) == 0 &&
|
| 468 | next_key == 0);
|
| 469 | assert(bpf_map_get_next_key(fd, &next_key, &next_key) == 0 &&
|
| 470 | next_key == 1);
|
| 471 | assert(bpf_map_get_next_key(fd, &next_key, &next_key) < 0 &&
|
| 472 | errno == ENOENT);
|
| 473 |
|
| 474 |
|
| 475 | key = 1;
|
| 476 | assert(bpf_map_delete_elem(fd, &key) < 0 && errno == EINVAL);
|
| 477 |
|
| 478 | close(fd);
|
| 479 | }
|
| 480 |
|
| 481 | static void test_arraymap_percpu_many_keys(void)
|
| 482 | {
|
| 483 | unsigned int nr_cpus = bpf_num_possible_cpus();
|
| 484 | BPF_DECLARE_PERCPU(long, values);
|
| 485 | |
| 486 | * allocator more than anything else
|
| 487 |
|
| 488 | unsigned int nr_keys = 2000;
|
| 489 | int key, fd, i;
|
| 490 |
|
| 491 | fd = bpf_map_create(BPF_MAP_TYPE_PERCPU_ARRAY, NULL, sizeof(key),
|
| 492 | sizeof(bpf_percpu(values, 0)), nr_keys, NULL);
|
| 493 | if (fd < 0) {
|
| 494 | printf("Failed to create per-cpu arraymap '%s'!\n",
|
| 495 | strerror(errno));
|
| 496 | exit(1);
|
| 497 | }
|
| 498 |
|
| 499 | for (i = 0; i < nr_cpus; i++)
|
| 500 | bpf_percpu(values, i) = i + 10;
|
| 501 |
|
| 502 | for (key = 0; key < nr_keys; key++)
|
| 503 | assert(bpf_map_update_elem(fd, &key, values, BPF_ANY) == 0);
|
| 504 |
|
| 505 | for (key = 0; key < nr_keys; key++) {
|
| 506 | for (i = 0; i < nr_cpus; i++)
|
| 507 | bpf_percpu(values, i) = 0;
|
| 508 |
|
| 509 | assert(bpf_map_lookup_elem(fd, &key, values) == 0);
|
| 510 |
|
| 511 | for (i = 0; i < nr_cpus; i++)
|
| 512 | assert(bpf_percpu(values, i) == i + 10);
|
| 513 | }
|
| 514 |
|
| 515 | close(fd);
|
| 516 | }
|
| 517 |
|
| 518 | static void test_devmap(unsigned int task, void *data)
|
| 519 | {
|
| 520 | int fd;
|
| 521 | __u32 key, value;
|
| 522 |
|
| 523 | fd = bpf_map_create(BPF_MAP_TYPE_DEVMAP, NULL, sizeof(key), sizeof(value), 2, NULL);
|
| 524 | if (fd < 0) {
|
| 525 | printf("Failed to create devmap '%s'!\n", strerror(errno));
|
| 526 | exit(1);
|
| 527 | }
|
| 528 |
|
| 529 | close(fd);
|
| 530 | }
|
| 531 |
|
| 532 | static void test_devmap_hash(unsigned int task, void *data)
|
| 533 | {
|
| 534 | int fd;
|
| 535 | __u32 key, value;
|
| 536 |
|
| 537 | fd = bpf_map_create(BPF_MAP_TYPE_DEVMAP_HASH, NULL, sizeof(key), sizeof(value), 2, NULL);
|
| 538 | if (fd < 0) {
|
| 539 | printf("Failed to create devmap_hash '%s'!\n", strerror(errno));
|
| 540 | exit(1);
|
| 541 | }
|
| 542 |
|
| 543 | close(fd);
|
| 544 | }
|
| 545 |
|
| 546 | static void test_queuemap(unsigned int task, void *data)
|
| 547 | {
|
| 548 | const int MAP_SIZE = 32;
|
| 549 | __u32 vals[MAP_SIZE + MAP_SIZE/2], val = 0;
|
| 550 | int fd, i;
|
| 551 |
|
| 552 |
|
| 553 | for (i = 0; i < MAP_SIZE + MAP_SIZE/2; i++)
|
| 554 | vals[i] = rand();
|
| 555 |
|
| 556 |
|
| 557 | fd = bpf_map_create(BPF_MAP_TYPE_QUEUE, NULL, 4, sizeof(val), MAP_SIZE, &map_opts);
|
| 558 | assert(fd < 0 && errno == EINVAL);
|
| 559 |
|
| 560 | fd = bpf_map_create(BPF_MAP_TYPE_QUEUE, NULL, 0, sizeof(val), MAP_SIZE, &map_opts);
|
| 561 |
|
| 562 | if (map_opts.map_flags & BPF_F_NO_PREALLOC) {
|
| 563 | assert(fd < 0 && errno == EINVAL);
|
| 564 | return;
|
| 565 | }
|
| 566 | if (fd < 0) {
|
| 567 | printf("Failed to create queuemap '%s'!\n", strerror(errno));
|
| 568 | exit(1);
|
| 569 | }
|
| 570 |
|
| 571 |
|
| 572 | for (i = 0; i < MAP_SIZE; i++)
|
| 573 | assert(bpf_map_update_elem(fd, NULL, &vals[i], 0) == 0);
|
| 574 |
|
| 575 |
|
| 576 | assert(bpf_map_update_elem(fd, NULL, &val, 0) < 0 &&
|
| 577 | errno == E2BIG);
|
| 578 |
|
| 579 |
|
| 580 | assert(bpf_map_lookup_elem(fd, NULL, &val) == 0 && val == vals[0]);
|
| 581 |
|
| 582 |
|
| 583 | for (i = MAP_SIZE; i < MAP_SIZE + MAP_SIZE/2; i++)
|
| 584 | assert(bpf_map_update_elem(fd, NULL, &vals[i], BPF_EXIST) == 0);
|
| 585 |
|
| 586 |
|
| 587 | for (i = MAP_SIZE/2; i < MAP_SIZE + MAP_SIZE/2; i++)
|
| 588 | assert(bpf_map_lookup_and_delete_elem(fd, NULL, &val) == 0 &&
|
| 589 | val == vals[i]);
|
| 590 |
|
| 591 |
|
| 592 | assert(bpf_map_lookup_and_delete_elem(fd, NULL, &val) < 0 &&
|
| 593 | errno == ENOENT);
|
| 594 |
|
| 595 |
|
| 596 | assert(bpf_map_delete_elem(fd, NULL) < 0 && errno == EINVAL);
|
| 597 | assert(bpf_map_get_next_key(fd, NULL, NULL) < 0 && errno == EINVAL);
|
| 598 |
|
| 599 | close(fd);
|
| 600 | }
|
| 601 |
|
| 602 | static void test_stackmap(unsigned int task, void *data)
|
| 603 | {
|
| 604 | const int MAP_SIZE = 32;
|
| 605 | __u32 vals[MAP_SIZE + MAP_SIZE/2], val = 0;
|
| 606 | int fd, i;
|
| 607 |
|
| 608 |
|
| 609 | for (i = 0; i < MAP_SIZE + MAP_SIZE/2; i++)
|
| 610 | vals[i] = rand();
|
| 611 |
|
| 612 |
|
| 613 | fd = bpf_map_create(BPF_MAP_TYPE_STACK, NULL, 4, sizeof(val), MAP_SIZE, &map_opts);
|
| 614 | assert(fd < 0 && errno == EINVAL);
|
| 615 |
|
| 616 | fd = bpf_map_create(BPF_MAP_TYPE_STACK, NULL, 0, sizeof(val), MAP_SIZE, &map_opts);
|
| 617 |
|
| 618 | if (map_opts.map_flags & BPF_F_NO_PREALLOC) {
|
| 619 | assert(fd < 0 && errno == EINVAL);
|
| 620 | return;
|
| 621 | }
|
| 622 | if (fd < 0) {
|
| 623 | printf("Failed to create stackmap '%s'!\n", strerror(errno));
|
| 624 | exit(1);
|
| 625 | }
|
| 626 |
|
| 627 |
|
| 628 | for (i = 0; i < MAP_SIZE; i++)
|
| 629 | assert(bpf_map_update_elem(fd, NULL, &vals[i], 0) == 0);
|
| 630 |
|
| 631 |
|
| 632 | assert(bpf_map_update_elem(fd, NULL, &val, 0) < 0 &&
|
| 633 | errno == E2BIG);
|
| 634 |
|
| 635 |
|
| 636 | assert(bpf_map_lookup_elem(fd, NULL, &val) == 0 && val == vals[i - 1]);
|
| 637 |
|
| 638 |
|
| 639 | for (i = MAP_SIZE; i < MAP_SIZE + MAP_SIZE/2; i++)
|
| 640 | assert(bpf_map_update_elem(fd, NULL, &vals[i], BPF_EXIST) == 0);
|
| 641 |
|
| 642 |
|
| 643 | for (i = MAP_SIZE + MAP_SIZE/2 - 1; i >= MAP_SIZE/2; i--)
|
| 644 | assert(bpf_map_lookup_and_delete_elem(fd, NULL, &val) == 0 &&
|
| 645 | val == vals[i]);
|
| 646 |
|
| 647 |
|
| 648 | assert(bpf_map_lookup_and_delete_elem(fd, NULL, &val) < 0 &&
|
| 649 | errno == ENOENT);
|
| 650 |
|
| 651 |
|
| 652 | assert(bpf_map_delete_elem(fd, NULL) < 0 && errno == EINVAL);
|
| 653 | assert(bpf_map_get_next_key(fd, NULL, NULL) < 0 && errno == EINVAL);
|
| 654 |
|
| 655 | close(fd);
|
| 656 | }
|
| 657 |
|
| 658 | #include <sys/ioctl.h>
|
| 659 | #include <arpa/inet.h>
|
| 660 | #include <sys/select.h>
|
| 661 | #include <linux/err.h>
|
| 662 | #define SOCKMAP_PARSE_PROG "./sockmap_parse_prog.bpf.o"
|
| 663 | #define SOCKMAP_VERDICT_PROG "./sockmap_verdict_prog.bpf.o"
|
| 664 | #define SOCKMAP_TCP_MSG_PROG "./sockmap_tcp_msg_prog.bpf.o"
|
| 665 | static void test_sockmap(unsigned int tasks, void *data)
|
| 666 | {
|
| 667 | struct bpf_map *bpf_map_rx, *bpf_map_tx, *bpf_map_msg, *bpf_map_break;
|
| 668 | int map_fd_msg = 0, map_fd_rx = 0, map_fd_tx = 0, map_fd_break;
|
| 669 | struct bpf_object *parse_obj, *verdict_obj, *msg_obj;
|
| 670 | int ports[] = {50200, 50201, 50202, 50204};
|
| 671 | int err, i, fd, udp, sfd[6] = {0xdeadbeef};
|
| 672 | u8 buf[20] = {0x0, 0x5, 0x3, 0x2, 0x1, 0x0};
|
| 673 | int parse_prog, verdict_prog, msg_prog;
|
| 674 | struct sockaddr_in addr;
|
| 675 | int one = 1, s, sc, rc;
|
| 676 | struct timeval to;
|
| 677 | __u32 key, value;
|
| 678 | pid_t pid[tasks];
|
| 679 | fd_set w;
|
| 680 |
|
| 681 |
|
| 682 | for (i = 0; i < 2; i++) {
|
| 683 | sfd[i] = socket(AF_INET, SOCK_STREAM, 0);
|
| 684 | if (sfd[i] < 0)
|
| 685 | goto out;
|
| 686 | err = setsockopt(sfd[i], SOL_SOCKET, SO_REUSEADDR,
|
| 687 | (char *)&one, sizeof(one));
|
| 688 | if (err) {
|
| 689 | printf("failed to setsockopt\n");
|
| 690 | goto out;
|
| 691 | }
|
| 692 | err = ioctl(sfd[i], FIONBIO, (char *)&one);
|
| 693 | if (err < 0) {
|
| 694 | printf("failed to ioctl\n");
|
| 695 | goto out;
|
| 696 | }
|
| 697 | memset(&addr, 0, sizeof(struct sockaddr_in));
|
| 698 | addr.sin_family = AF_INET;
|
| 699 | addr.sin_addr.s_addr = inet_addr("127.0.0.1");
|
| 700 | addr.sin_port = htons(ports[i]);
|
| 701 | err = bind(sfd[i], (struct sockaddr *)&addr, sizeof(addr));
|
| 702 | if (err < 0) {
|
| 703 | printf("failed to bind: err %i: %i:%i\n",
|
| 704 | err, i, sfd[i]);
|
| 705 | goto out;
|
| 706 | }
|
| 707 | err = listen(sfd[i], 32);
|
| 708 | if (err < 0) {
|
| 709 | printf("failed to listen\n");
|
| 710 | goto out;
|
| 711 | }
|
| 712 | }
|
| 713 |
|
| 714 | for (i = 2; i < 4; i++) {
|
| 715 | sfd[i] = socket(AF_INET, SOCK_STREAM, 0);
|
| 716 | if (sfd[i] < 0)
|
| 717 | goto out;
|
| 718 | err = setsockopt(sfd[i], SOL_SOCKET, SO_REUSEADDR,
|
| 719 | (char *)&one, sizeof(one));
|
| 720 | if (err) {
|
| 721 | printf("set sock opt\n");
|
| 722 | goto out;
|
| 723 | }
|
| 724 | memset(&addr, 0, sizeof(struct sockaddr_in));
|
| 725 | addr.sin_family = AF_INET;
|
| 726 | addr.sin_addr.s_addr = inet_addr("127.0.0.1");
|
| 727 | addr.sin_port = htons(ports[i - 2]);
|
| 728 | err = connect(sfd[i], (struct sockaddr *)&addr, sizeof(addr));
|
| 729 | if (err) {
|
| 730 | printf("failed to connect\n");
|
| 731 | goto out;
|
| 732 | }
|
| 733 | }
|
| 734 |
|
| 735 |
|
| 736 | for (i = 4; i < 6; i++) {
|
| 737 | sfd[i] = accept(sfd[i - 4], NULL, NULL);
|
| 738 | if (sfd[i] < 0) {
|
| 739 | printf("accept failed\n");
|
| 740 | goto out;
|
| 741 | }
|
| 742 | }
|
| 743 |
|
| 744 |
|
| 745 | fd = bpf_map_create(BPF_MAP_TYPE_SOCKMAP, NULL,
|
| 746 | sizeof(key), sizeof(value),
|
| 747 | 6, NULL);
|
| 748 | if (fd < 0) {
|
| 749 | if (!libbpf_probe_bpf_map_type(BPF_MAP_TYPE_SOCKMAP, NULL)) {
|
| 750 | printf("%s SKIP (unsupported map type BPF_MAP_TYPE_SOCKMAP)\n",
|
| 751 | __func__);
|
| 752 | skips++;
|
| 753 | for (i = 0; i < 6; i++)
|
| 754 | close(sfd[i]);
|
| 755 | return;
|
| 756 | }
|
| 757 |
|
| 758 | printf("Failed to create sockmap %i\n", fd);
|
| 759 | goto out_sockmap;
|
| 760 | }
|
| 761 |
|
| 762 |
|
| 763 | udp = socket(AF_INET, SOCK_DGRAM, 0);
|
| 764 | CHECK(udp < 0, "socket(AF_INET, SOCK_DGRAM)", "errno:%d\n", errno);
|
| 765 | err = bpf_map_update_elem(fd, &(int){0}, &udp, BPF_ANY);
|
| 766 | close(udp);
|
| 767 | if (!err) {
|
| 768 | printf("Unexpectedly succeeded unbound UDP update '0:%i'\n", udp);
|
| 769 | goto out_sockmap;
|
| 770 | }
|
| 771 |
|
| 772 |
|
| 773 | for (i = 0; i < 6; i++) {
|
| 774 | err = bpf_map_update_elem(fd, &i, &sfd[i], BPF_ANY);
|
| 775 | if (err) {
|
| 776 | printf("Failed noprog update sockmap '%i:%i'\n",
|
| 777 | i, sfd[i]);
|
| 778 | goto out_sockmap;
|
| 779 | }
|
| 780 | }
|
| 781 |
|
| 782 |
|
| 783 | err = bpf_prog_attach(-1, fd, BPF_SK_SKB_STREAM_PARSER, 0);
|
| 784 | if (!err) {
|
| 785 | printf("Failed invalid parser prog attach\n");
|
| 786 | goto out_sockmap;
|
| 787 | }
|
| 788 |
|
| 789 | err = bpf_prog_attach(-1, fd, BPF_SK_SKB_STREAM_VERDICT, 0);
|
| 790 | if (!err) {
|
| 791 | printf("Failed invalid verdict prog attach\n");
|
| 792 | goto out_sockmap;
|
| 793 | }
|
| 794 |
|
| 795 | err = bpf_prog_attach(-1, fd, BPF_SK_MSG_VERDICT, 0);
|
| 796 | if (!err) {
|
| 797 | printf("Failed invalid msg verdict prog attach\n");
|
| 798 | goto out_sockmap;
|
| 799 | }
|
| 800 |
|
| 801 | err = bpf_prog_attach(-1, fd, __MAX_BPF_ATTACH_TYPE, 0);
|
| 802 | if (!err) {
|
| 803 | printf("Failed unknown prog attach\n");
|
| 804 | goto out_sockmap;
|
| 805 | }
|
| 806 |
|
| 807 | err = bpf_prog_detach(fd, BPF_SK_SKB_STREAM_PARSER);
|
| 808 | if (!err) {
|
| 809 | printf("Failed empty parser prog detach\n");
|
| 810 | goto out_sockmap;
|
| 811 | }
|
| 812 |
|
| 813 | err = bpf_prog_detach(fd, BPF_SK_SKB_STREAM_VERDICT);
|
| 814 | if (!err) {
|
| 815 | printf("Failed empty verdict prog detach\n");
|
| 816 | goto out_sockmap;
|
| 817 | }
|
| 818 |
|
| 819 | err = bpf_prog_detach(fd, BPF_SK_MSG_VERDICT);
|
| 820 | if (!err) {
|
| 821 | printf("Failed empty msg verdict prog detach\n");
|
| 822 | goto out_sockmap;
|
| 823 | }
|
| 824 |
|
| 825 | err = bpf_prog_detach(fd, __MAX_BPF_ATTACH_TYPE);
|
| 826 | if (!err) {
|
| 827 | printf("Detach invalid prog successful\n");
|
| 828 | goto out_sockmap;
|
| 829 | }
|
| 830 |
|
| 831 |
|
| 832 | err = bpf_prog_test_load(SOCKMAP_PARSE_PROG,
|
| 833 | BPF_PROG_TYPE_SK_SKB, &parse_obj, &parse_prog);
|
| 834 | if (err) {
|
| 835 | printf("Failed to load SK_SKB parse prog\n");
|
| 836 | goto out_sockmap;
|
| 837 | }
|
| 838 |
|
| 839 | err = bpf_prog_test_load(SOCKMAP_TCP_MSG_PROG,
|
| 840 | BPF_PROG_TYPE_SK_MSG, &msg_obj, &msg_prog);
|
| 841 | if (err) {
|
| 842 | printf("Failed to load SK_SKB msg prog\n");
|
| 843 | goto out_sockmap;
|
| 844 | }
|
| 845 |
|
| 846 | err = bpf_prog_test_load(SOCKMAP_VERDICT_PROG,
|
| 847 | BPF_PROG_TYPE_SK_SKB, &verdict_obj, &verdict_prog);
|
| 848 | if (err) {
|
| 849 | printf("Failed to load SK_SKB verdict prog\n");
|
| 850 | goto out_sockmap;
|
| 851 | }
|
| 852 |
|
| 853 | bpf_map_rx = bpf_object__find_map_by_name(verdict_obj, "sock_map_rx");
|
| 854 | if (!bpf_map_rx) {
|
| 855 | printf("Failed to load map rx from verdict prog\n");
|
| 856 | goto out_sockmap;
|
| 857 | }
|
| 858 |
|
| 859 | map_fd_rx = bpf_map__fd(bpf_map_rx);
|
| 860 | if (map_fd_rx < 0) {
|
| 861 | printf("Failed to get map rx fd\n");
|
| 862 | goto out_sockmap;
|
| 863 | }
|
| 864 |
|
| 865 | bpf_map_tx = bpf_object__find_map_by_name(verdict_obj, "sock_map_tx");
|
| 866 | if (!bpf_map_tx) {
|
| 867 | printf("Failed to load map tx from verdict prog\n");
|
| 868 | goto out_sockmap;
|
| 869 | }
|
| 870 |
|
| 871 | map_fd_tx = bpf_map__fd(bpf_map_tx);
|
| 872 | if (map_fd_tx < 0) {
|
| 873 | printf("Failed to get map tx fd\n");
|
| 874 | goto out_sockmap;
|
| 875 | }
|
| 876 |
|
| 877 | bpf_map_msg = bpf_object__find_map_by_name(verdict_obj, "sock_map_msg");
|
| 878 | if (!bpf_map_msg) {
|
| 879 | printf("Failed to load map msg from msg_verdict prog\n");
|
| 880 | goto out_sockmap;
|
| 881 | }
|
| 882 |
|
| 883 | map_fd_msg = bpf_map__fd(bpf_map_msg);
|
| 884 | if (map_fd_msg < 0) {
|
| 885 | printf("Failed to get map msg fd\n");
|
| 886 | goto out_sockmap;
|
| 887 | }
|
| 888 |
|
| 889 | bpf_map_break = bpf_object__find_map_by_name(verdict_obj, "sock_map_break");
|
| 890 | if (!bpf_map_break) {
|
| 891 | printf("Failed to load map tx from verdict prog\n");
|
| 892 | goto out_sockmap;
|
| 893 | }
|
| 894 |
|
| 895 | map_fd_break = bpf_map__fd(bpf_map_break);
|
| 896 | if (map_fd_break < 0) {
|
| 897 | printf("Failed to get map tx fd\n");
|
| 898 | goto out_sockmap;
|
| 899 | }
|
| 900 |
|
| 901 | err = bpf_prog_attach(parse_prog, map_fd_break,
|
| 902 | BPF_SK_SKB_STREAM_PARSER, 0);
|
| 903 | if (!err) {
|
| 904 | printf("Allowed attaching SK_SKB program to invalid map\n");
|
| 905 | goto out_sockmap;
|
| 906 | }
|
| 907 |
|
| 908 | err = bpf_prog_attach(parse_prog, map_fd_rx,
|
| 909 | BPF_SK_SKB_STREAM_PARSER, 0);
|
| 910 | if (err) {
|
| 911 | printf("Failed stream parser bpf prog attach\n");
|
| 912 | goto out_sockmap;
|
| 913 | }
|
| 914 |
|
| 915 | err = bpf_prog_attach(verdict_prog, map_fd_rx,
|
| 916 | BPF_SK_SKB_STREAM_VERDICT, 0);
|
| 917 | if (err) {
|
| 918 | printf("Failed stream verdict bpf prog attach\n");
|
| 919 | goto out_sockmap;
|
| 920 | }
|
| 921 |
|
| 922 | err = bpf_prog_attach(msg_prog, map_fd_msg, BPF_SK_MSG_VERDICT, 0);
|
| 923 | if (err) {
|
| 924 | printf("Failed msg verdict bpf prog attach\n");
|
| 925 | goto out_sockmap;
|
| 926 | }
|
| 927 |
|
| 928 | err = bpf_prog_attach(verdict_prog, map_fd_rx,
|
| 929 | __MAX_BPF_ATTACH_TYPE, 0);
|
| 930 | if (!err) {
|
| 931 | printf("Attached unknown bpf prog\n");
|
| 932 | goto out_sockmap;
|
| 933 | }
|
| 934 |
|
| 935 |
|
| 936 | for (i = 2; i < 6; i++) {
|
| 937 | err = bpf_map_update_elem(map_fd_rx, &i, &sfd[i], BPF_ANY);
|
| 938 | if (err) {
|
| 939 | printf("Failed map_fd_rx update sockmap %i '%i:%i'\n",
|
| 940 | err, i, sfd[i]);
|
| 941 | goto out_sockmap;
|
| 942 | }
|
| 943 | err = bpf_map_update_elem(map_fd_tx, &i, &sfd[i], BPF_ANY);
|
| 944 | if (err) {
|
| 945 | printf("Failed map_fd_tx update sockmap %i '%i:%i'\n",
|
| 946 | err, i, sfd[i]);
|
| 947 | goto out_sockmap;
|
| 948 | }
|
| 949 | }
|
| 950 |
|
| 951 |
|
| 952 | for (i = 2; i < 4; i++) {
|
| 953 | err = bpf_map_delete_elem(map_fd_rx, &i);
|
| 954 | if (err) {
|
| 955 | printf("Failed delete sockmap rx %i '%i:%i'\n",
|
| 956 | err, i, sfd[i]);
|
| 957 | goto out_sockmap;
|
| 958 | }
|
| 959 | err = bpf_map_delete_elem(map_fd_tx, &i);
|
| 960 | if (err) {
|
| 961 | printf("Failed delete sockmap tx %i '%i:%i'\n",
|
| 962 | err, i, sfd[i]);
|
| 963 | goto out_sockmap;
|
| 964 | }
|
| 965 | }
|
| 966 |
|
| 967 |
|
| 968 | i = 0;
|
| 969 | err = bpf_map_update_elem(map_fd_msg, &i, &sfd[2], BPF_ANY);
|
| 970 | if (err) {
|
| 971 | printf("Failed map_fd_msg update sockmap %i\n", err);
|
| 972 | goto out_sockmap;
|
| 973 | }
|
| 974 |
|
| 975 |
|
| 976 | for (i = 0; i < 2; i++) {
|
| 977 | buf[0] = i;
|
| 978 | buf[1] = 0x5;
|
| 979 | sc = send(sfd[2], buf, 20, 0);
|
| 980 | if (sc < 0) {
|
| 981 | printf("Failed sockmap send\n");
|
| 982 | goto out_sockmap;
|
| 983 | }
|
| 984 |
|
| 985 | FD_ZERO(&w);
|
| 986 | FD_SET(sfd[3], &w);
|
| 987 | to.tv_sec = 30;
|
| 988 | to.tv_usec = 0;
|
| 989 | s = select(sfd[3] + 1, &w, NULL, NULL, &to);
|
| 990 | if (s == -1) {
|
| 991 | perror("Failed sockmap select()");
|
| 992 | goto out_sockmap;
|
| 993 | } else if (!s) {
|
| 994 | printf("Failed sockmap unexpected timeout\n");
|
| 995 | goto out_sockmap;
|
| 996 | }
|
| 997 |
|
| 998 | if (!FD_ISSET(sfd[3], &w)) {
|
| 999 | printf("Failed sockmap select/recv\n");
|
| 1000 | goto out_sockmap;
|
| 1001 | }
|
| 1002 |
|
| 1003 | rc = recv(sfd[3], buf, sizeof(buf), 0);
|
| 1004 | if (rc < 0) {
|
| 1005 | printf("Failed sockmap recv\n");
|
| 1006 | goto out_sockmap;
|
| 1007 | }
|
| 1008 | }
|
| 1009 |
|
| 1010 |
|
| 1011 | buf[0] = 1;
|
| 1012 | buf[1] = 12;
|
| 1013 | sc = send(sfd[2], buf, 20, 0);
|
| 1014 | if (sc < 0) {
|
| 1015 | printf("Failed sockmap send\n");
|
| 1016 | goto out_sockmap;
|
| 1017 | }
|
| 1018 |
|
| 1019 |
|
| 1020 | i = 2;
|
| 1021 | err = bpf_map_update_elem(fd, &i, &sfd[i], BPF_NOEXIST);
|
| 1022 | if (!err) {
|
| 1023 | printf("Failed allowed sockmap dup slot BPF_NOEXIST\n");
|
| 1024 | goto out_sockmap;
|
| 1025 | }
|
| 1026 |
|
| 1027 | err = bpf_map_update_elem(fd, &i, &sfd[i], BPF_ANY);
|
| 1028 | if (err) {
|
| 1029 | printf("Failed sockmap update new slot BPF_ANY\n");
|
| 1030 | goto out_sockmap;
|
| 1031 | }
|
| 1032 |
|
| 1033 | err = bpf_map_update_elem(fd, &i, &sfd[i], BPF_EXIST);
|
| 1034 | if (err) {
|
| 1035 | printf("Failed sockmap update new slot BPF_EXIST\n");
|
| 1036 | goto out_sockmap;
|
| 1037 | }
|
| 1038 |
|
| 1039 |
|
| 1040 | for (i = 2; i < 6; i++) {
|
| 1041 | err = bpf_map_delete_elem(fd, &i);
|
| 1042 | if (err) {
|
| 1043 | printf("Failed delete sockmap %i '%i:%i'\n",
|
| 1044 | err, i, sfd[i]);
|
| 1045 | }
|
| 1046 | }
|
| 1047 |
|
| 1048 |
|
| 1049 | err = bpf_prog_attach(parse_prog, fd,
|
| 1050 | BPF_SK_SKB_STREAM_PARSER, 0);
|
| 1051 | if (err) {
|
| 1052 | printf("Failed fd bpf parse prog attach\n");
|
| 1053 | goto out_sockmap;
|
| 1054 | }
|
| 1055 | err = bpf_prog_attach(verdict_prog, fd,
|
| 1056 | BPF_SK_SKB_STREAM_VERDICT, 0);
|
| 1057 | if (err) {
|
| 1058 | printf("Failed fd bpf verdict prog attach\n");
|
| 1059 | goto out_sockmap;
|
| 1060 | }
|
| 1061 |
|
| 1062 | for (i = 4; i < 6; i++) {
|
| 1063 | err = bpf_map_update_elem(fd, &i, &sfd[i], BPF_ANY);
|
| 1064 | if (!err) {
|
| 1065 | printf("Failed allowed duplicate programs in update ANY sockmap %i '%i:%i'\n",
|
| 1066 | err, i, sfd[i]);
|
| 1067 | goto out_sockmap;
|
| 1068 | }
|
| 1069 | err = bpf_map_update_elem(fd, &i, &sfd[i], BPF_NOEXIST);
|
| 1070 | if (!err) {
|
| 1071 | printf("Failed allowed duplicate program in update NOEXIST sockmap %i '%i:%i'\n",
|
| 1072 | err, i, sfd[i]);
|
| 1073 | goto out_sockmap;
|
| 1074 | }
|
| 1075 | err = bpf_map_update_elem(fd, &i, &sfd[i], BPF_EXIST);
|
| 1076 | if (!err) {
|
| 1077 | printf("Failed allowed duplicate program in update EXIST sockmap %i '%i:%i'\n",
|
| 1078 | err, i, sfd[i]);
|
| 1079 | goto out_sockmap;
|
| 1080 | }
|
| 1081 | }
|
| 1082 |
|
| 1083 |
|
| 1084 | for (i = 0; i < tasks; i++) {
|
| 1085 | pid[i] = fork();
|
| 1086 | if (pid[i] == 0) {
|
| 1087 | for (i = 0; i < 6; i++) {
|
| 1088 | bpf_map_delete_elem(map_fd_tx, &i);
|
| 1089 | bpf_map_delete_elem(map_fd_rx, &i);
|
| 1090 | bpf_map_update_elem(map_fd_tx, &i,
|
| 1091 | &sfd[i], BPF_ANY);
|
| 1092 | bpf_map_update_elem(map_fd_rx, &i,
|
| 1093 | &sfd[i], BPF_ANY);
|
| 1094 | }
|
| 1095 | exit(0);
|
| 1096 | } else if (pid[i] == -1) {
|
| 1097 | printf("Couldn't spawn #%d process!\n", i);
|
| 1098 | exit(1);
|
| 1099 | }
|
| 1100 | }
|
| 1101 |
|
| 1102 | for (i = 0; i < tasks; i++) {
|
| 1103 | int status;
|
| 1104 |
|
| 1105 | assert(waitpid(pid[i], &status, 0) == pid[i]);
|
| 1106 | assert(status == 0);
|
| 1107 | }
|
| 1108 |
|
| 1109 | err = bpf_prog_detach2(parse_prog, map_fd_rx, __MAX_BPF_ATTACH_TYPE);
|
| 1110 | if (!err) {
|
| 1111 | printf("Detached an invalid prog type.\n");
|
| 1112 | goto out_sockmap;
|
| 1113 | }
|
| 1114 |
|
| 1115 | err = bpf_prog_detach2(parse_prog, map_fd_rx, BPF_SK_SKB_STREAM_PARSER);
|
| 1116 | if (err) {
|
| 1117 | printf("Failed parser prog detach\n");
|
| 1118 | goto out_sockmap;
|
| 1119 | }
|
| 1120 |
|
| 1121 | err = bpf_prog_detach2(verdict_prog, map_fd_rx, BPF_SK_SKB_STREAM_VERDICT);
|
| 1122 | if (err) {
|
| 1123 | printf("Failed parser prog detach\n");
|
| 1124 | goto out_sockmap;
|
| 1125 | }
|
| 1126 |
|
| 1127 |
|
| 1128 | for (i = 0; i < 6; i++) {
|
| 1129 | bpf_map_delete_elem(map_fd_tx, &i);
|
| 1130 | bpf_map_delete_elem(map_fd_rx, &i);
|
| 1131 | close(sfd[i]);
|
| 1132 | }
|
| 1133 | close(fd);
|
| 1134 | close(map_fd_rx);
|
| 1135 | bpf_object__close(parse_obj);
|
| 1136 | bpf_object__close(msg_obj);
|
| 1137 | bpf_object__close(verdict_obj);
|
| 1138 | return;
|
| 1139 | out:
|
| 1140 | for (i = 0; i < 6; i++)
|
| 1141 | close(sfd[i]);
|
| 1142 | printf("Failed to create sockmap '%i:%s'!\n", i, strerror(errno));
|
| 1143 | exit(1);
|
| 1144 | out_sockmap:
|
| 1145 | for (i = 0; i < 6; i++) {
|
| 1146 | if (map_fd_tx)
|
| 1147 | bpf_map_delete_elem(map_fd_tx, &i);
|
| 1148 | if (map_fd_rx)
|
| 1149 | bpf_map_delete_elem(map_fd_rx, &i);
|
| 1150 | close(sfd[i]);
|
| 1151 | }
|
| 1152 | close(fd);
|
| 1153 | exit(1);
|
| 1154 | }
|
| 1155 |
|
| 1156 | #define MAPINMAP_PROG "./test_map_in_map.bpf.o"
|
| 1157 | #define MAPINMAP_INVALID_PROG "./test_map_in_map_invalid.bpf.o"
|
| 1158 | static void test_map_in_map(void)
|
| 1159 | {
|
| 1160 | struct bpf_object *obj;
|
| 1161 | struct bpf_map *map;
|
| 1162 | int mim_fd, fd, err;
|
| 1163 | int pos = 0;
|
| 1164 | struct bpf_map_info info = {};
|
| 1165 | __u32 len = sizeof(info);
|
| 1166 | __u32 id = 0;
|
| 1167 | libbpf_print_fn_t old_print_fn;
|
| 1168 |
|
| 1169 | obj = bpf_object__open(MAPINMAP_PROG);
|
| 1170 |
|
| 1171 | fd = bpf_map_create(BPF_MAP_TYPE_HASH, NULL, sizeof(int), sizeof(int), 2, NULL);
|
| 1172 | if (fd < 0) {
|
| 1173 | printf("Failed to create hashmap '%s'!\n", strerror(errno));
|
| 1174 | exit(1);
|
| 1175 | }
|
| 1176 |
|
| 1177 | map = bpf_object__find_map_by_name(obj, "mim_array");
|
| 1178 | if (!map) {
|
| 1179 | printf("Failed to load array of maps from test prog\n");
|
| 1180 | goto out_map_in_map;
|
| 1181 | }
|
| 1182 | err = bpf_map__set_inner_map_fd(map, fd);
|
| 1183 | if (err) {
|
| 1184 | printf("Failed to set inner_map_fd for array of maps\n");
|
| 1185 | goto out_map_in_map;
|
| 1186 | }
|
| 1187 |
|
| 1188 | map = bpf_object__find_map_by_name(obj, "mim_hash");
|
| 1189 | if (!map) {
|
| 1190 | printf("Failed to load hash of maps from test prog\n");
|
| 1191 | goto out_map_in_map;
|
| 1192 | }
|
| 1193 | err = bpf_map__set_inner_map_fd(map, fd);
|
| 1194 | if (err) {
|
| 1195 | printf("Failed to set inner_map_fd for hash of maps\n");
|
| 1196 | goto out_map_in_map;
|
| 1197 | }
|
| 1198 |
|
| 1199 | err = bpf_object__load(obj);
|
| 1200 | if (err) {
|
| 1201 | printf("Failed to load test prog\n");
|
| 1202 | goto out_map_in_map;
|
| 1203 | }
|
| 1204 |
|
| 1205 | map = bpf_object__find_map_by_name(obj, "mim_array");
|
| 1206 | if (!map) {
|
| 1207 | printf("Failed to load array of maps from test prog\n");
|
| 1208 | goto out_map_in_map;
|
| 1209 | }
|
| 1210 | mim_fd = bpf_map__fd(map);
|
| 1211 | if (mim_fd < 0) {
|
| 1212 | printf("Failed to get descriptor for array of maps\n");
|
| 1213 | goto out_map_in_map;
|
| 1214 | }
|
| 1215 |
|
| 1216 | err = bpf_map_update_elem(mim_fd, &pos, &fd, 0);
|
| 1217 | if (err) {
|
| 1218 | printf("Failed to update array of maps\n");
|
| 1219 | goto out_map_in_map;
|
| 1220 | }
|
| 1221 |
|
| 1222 | map = bpf_object__find_map_by_name(obj, "mim_hash");
|
| 1223 | if (!map) {
|
| 1224 | printf("Failed to load hash of maps from test prog\n");
|
| 1225 | goto out_map_in_map;
|
| 1226 | }
|
| 1227 | mim_fd = bpf_map__fd(map);
|
| 1228 | if (mim_fd < 0) {
|
| 1229 | printf("Failed to get descriptor for hash of maps\n");
|
| 1230 | goto out_map_in_map;
|
| 1231 | }
|
| 1232 |
|
| 1233 | err = bpf_map_update_elem(mim_fd, &pos, &fd, 0);
|
| 1234 | if (err) {
|
| 1235 | printf("Failed to update hash of maps\n");
|
| 1236 | goto out_map_in_map;
|
| 1237 | }
|
| 1238 |
|
| 1239 | close(fd);
|
| 1240 | fd = -1;
|
| 1241 | bpf_object__close(obj);
|
| 1242 |
|
| 1243 |
|
| 1244 | obj = bpf_object__open(MAPINMAP_INVALID_PROG);
|
| 1245 | err = libbpf_get_error(obj);
|
| 1246 | if (err) {
|
| 1247 | printf("Failed to load %s program: %d %d",
|
| 1248 | MAPINMAP_INVALID_PROG, err, errno);
|
| 1249 | goto out_map_in_map;
|
| 1250 | }
|
| 1251 |
|
| 1252 | map = bpf_object__find_map_by_name(obj, "mim");
|
| 1253 | if (!map) {
|
| 1254 | printf("Failed to load array of maps from test prog\n");
|
| 1255 | goto out_map_in_map;
|
| 1256 | }
|
| 1257 |
|
| 1258 | old_print_fn = libbpf_set_print(NULL);
|
| 1259 |
|
| 1260 | err = bpf_object__load(obj);
|
| 1261 | if (!err) {
|
| 1262 | printf("Loading obj supposed to fail\n");
|
| 1263 | goto out_map_in_map;
|
| 1264 | }
|
| 1265 |
|
| 1266 | libbpf_set_print(old_print_fn);
|
| 1267 |
|
| 1268 | |
| 1269 | * ("mim.internal") has been destroyed.
|
| 1270 |
|
| 1271 | while (true) {
|
| 1272 | err = bpf_map_get_next_id(id, &id);
|
| 1273 | if (err) {
|
| 1274 | if (errno == ENOENT)
|
| 1275 | break;
|
| 1276 | printf("Failed to get next map: %d", errno);
|
| 1277 | goto out_map_in_map;
|
| 1278 | }
|
| 1279 |
|
| 1280 | fd = bpf_map_get_fd_by_id(id);
|
| 1281 | if (fd < 0) {
|
| 1282 | if (errno == ENOENT)
|
| 1283 | continue;
|
| 1284 | printf("Failed to get map by id %u: %d", id, errno);
|
| 1285 | goto out_map_in_map;
|
| 1286 | }
|
| 1287 |
|
| 1288 | err = bpf_map_get_info_by_fd(fd, &info, &len);
|
| 1289 | if (err) {
|
| 1290 | printf("Failed to get map info by fd %d: %d", fd,
|
| 1291 | errno);
|
| 1292 | goto out_map_in_map;
|
| 1293 | }
|
| 1294 |
|
| 1295 | if (!strcmp(info.name, "mim.inner")) {
|
| 1296 | printf("Inner map mim.inner was not destroyed\n");
|
| 1297 | goto out_map_in_map;
|
| 1298 | }
|
| 1299 |
|
| 1300 | close(fd);
|
| 1301 | }
|
| 1302 |
|
| 1303 | bpf_object__close(obj);
|
| 1304 | return;
|
| 1305 |
|
| 1306 | out_map_in_map:
|
| 1307 | if (fd >= 0)
|
| 1308 | close(fd);
|
| 1309 | exit(1);
|
| 1310 | }
|
| 1311 |
|
| 1312 | #define MAP_SIZE (32 * 1024)
|
| 1313 |
|
| 1314 | static void test_map_large(void)
|
| 1315 | {
|
| 1316 |
|
| 1317 | struct bigkey {
|
| 1318 | int a;
|
| 1319 | char b[4096];
|
| 1320 | long long c;
|
| 1321 | } key;
|
| 1322 | int fd, i, value;
|
| 1323 |
|
| 1324 | fd = bpf_map_create(BPF_MAP_TYPE_HASH, NULL, sizeof(key), sizeof(value),
|
| 1325 | MAP_SIZE, &map_opts);
|
| 1326 | if (fd < 0) {
|
| 1327 | printf("Failed to create large map '%s'!\n", strerror(errno));
|
| 1328 | exit(1);
|
| 1329 | }
|
| 1330 |
|
| 1331 | for (i = 0; i < MAP_SIZE; i++) {
|
| 1332 | key = (struct bigkey) { .c = i };
|
| 1333 | value = i;
|
| 1334 |
|
| 1335 | assert(bpf_map_update_elem(fd, &key, &value, BPF_NOEXIST) == 0);
|
| 1336 | }
|
| 1337 |
|
| 1338 | key.c = -1;
|
| 1339 | assert(bpf_map_update_elem(fd, &key, &value, BPF_NOEXIST) < 0 &&
|
| 1340 | errno == E2BIG);
|
| 1341 |
|
| 1342 |
|
| 1343 | assert(bpf_map_get_next_key(fd, NULL, &key) == 0);
|
| 1344 | key.c = -1;
|
| 1345 | for (i = 0; i < MAP_SIZE; i++)
|
| 1346 | assert(bpf_map_get_next_key(fd, &key, &key) == 0);
|
| 1347 | assert(bpf_map_get_next_key(fd, &key, &key) < 0 && errno == ENOENT);
|
| 1348 |
|
| 1349 | key.c = 0;
|
| 1350 | assert(bpf_map_lookup_elem(fd, &key, &value) == 0 && value == 0);
|
| 1351 | key.a = 1;
|
| 1352 | assert(bpf_map_lookup_elem(fd, &key, &value) < 0 && errno == ENOENT);
|
| 1353 |
|
| 1354 | close(fd);
|
| 1355 | }
|
| 1356 |
|
| 1357 | #define run_parallel(N, FN, DATA) \
|
| 1358 | printf("Fork %u tasks to '" #FN "'\n", N); \
|
| 1359 | __run_parallel(N, FN, DATA)
|
| 1360 |
|
| 1361 | static void __run_parallel(unsigned int tasks,
|
| 1362 | void (*fn)(unsigned int task, void *data),
|
| 1363 | void *data)
|
| 1364 | {
|
| 1365 | pid_t pid[tasks];
|
| 1366 | int i;
|
| 1367 |
|
| 1368 | fflush(stdout);
|
| 1369 |
|
| 1370 | for (i = 0; i < tasks; i++) {
|
| 1371 | pid[i] = fork();
|
| 1372 | if (pid[i] == 0) {
|
| 1373 | fn(i, data);
|
| 1374 | exit(0);
|
| 1375 | } else if (pid[i] == -1) {
|
| 1376 | printf("Couldn't spawn #%d process!\n", i);
|
| 1377 | exit(1);
|
| 1378 | }
|
| 1379 | }
|
| 1380 |
|
| 1381 | for (i = 0; i < tasks; i++) {
|
| 1382 | int status;
|
| 1383 |
|
| 1384 | assert(waitpid(pid[i], &status, 0) == pid[i]);
|
| 1385 | assert(status == 0);
|
| 1386 | }
|
| 1387 | }
|
| 1388 |
|
| 1389 | static void test_map_stress(void)
|
| 1390 | {
|
| 1391 | run_parallel(100, test_hashmap_walk, NULL);
|
| 1392 | run_parallel(100, test_hashmap, NULL);
|
| 1393 | run_parallel(100, test_hashmap_percpu, NULL);
|
| 1394 | run_parallel(100, test_hashmap_sizes, NULL);
|
| 1395 |
|
| 1396 | run_parallel(100, test_arraymap, NULL);
|
| 1397 | run_parallel(100, test_arraymap_percpu, NULL);
|
| 1398 | }
|
| 1399 |
|
| 1400 | #define TASKS 100
|
| 1401 |
|
| 1402 | #define DO_UPDATE 1
|
| 1403 | #define DO_DELETE 0
|
| 1404 |
|
| 1405 | #define MAX_DELAY_US 50000
|
| 1406 | #define MIN_DELAY_RANGE_US 5000
|
| 1407 |
|
| 1408 | int map_update_retriable(int map_fd, const void *key, const void *value, int flags, int attempts,
|
| 1409 | retry_for_error_fn need_retry)
|
| 1410 | {
|
| 1411 | int delay = rand() % MIN_DELAY_RANGE_US;
|
| 1412 |
|
| 1413 | while (bpf_map_update_elem(map_fd, key, value, flags)) {
|
| 1414 | if (!attempts || !need_retry(errno))
|
| 1415 | return -errno;
|
| 1416 |
|
| 1417 | if (delay <= MAX_DELAY_US / 2)
|
| 1418 | delay *= 2;
|
| 1419 |
|
| 1420 | usleep(delay);
|
| 1421 | attempts--;
|
| 1422 | }
|
| 1423 |
|
| 1424 | return 0;
|
| 1425 | }
|
| 1426 |
|
| 1427 | static int map_delete_retriable(int map_fd, const void *key, int attempts)
|
| 1428 | {
|
| 1429 | int delay = rand() % MIN_DELAY_RANGE_US;
|
| 1430 |
|
| 1431 | while (bpf_map_delete_elem(map_fd, key)) {
|
| 1432 | if (!attempts || (errno != EAGAIN && errno != EBUSY))
|
| 1433 | return -errno;
|
| 1434 |
|
| 1435 | if (delay <= MAX_DELAY_US / 2)
|
| 1436 | delay *= 2;
|
| 1437 |
|
| 1438 | usleep(delay);
|
| 1439 | attempts--;
|
| 1440 | }
|
| 1441 |
|
| 1442 | return 0;
|
| 1443 | }
|
| 1444 |
|
| 1445 | static void test_update_delete(unsigned int fn, void *data)
|
| 1446 | {
|
| 1447 | int do_update = ((int *)data)[1];
|
| 1448 | int fd = ((int *)data)[0];
|
| 1449 | int i, key, value, err;
|
| 1450 |
|
| 1451 | if (fn & 1)
|
| 1452 | test_hashmap_walk(fn, NULL);
|
| 1453 | for (i = fn; i < MAP_SIZE; i += TASKS) {
|
| 1454 | key = value = i;
|
| 1455 |
|
| 1456 | if (do_update) {
|
| 1457 | err = map_update_retriable(fd, &key, &value, BPF_NOEXIST, MAP_RETRIES,
|
| 1458 | can_retry);
|
| 1459 | if (err)
|
| 1460 | printf("error %d %d\n", err, errno);
|
| 1461 | assert(err == 0);
|
| 1462 | err = map_update_retriable(fd, &key, &value, BPF_EXIST, MAP_RETRIES,
|
| 1463 | can_retry);
|
| 1464 | if (err)
|
| 1465 | printf("error %d %d\n", err, errno);
|
| 1466 | assert(err == 0);
|
| 1467 | } else {
|
| 1468 | err = map_delete_retriable(fd, &key, MAP_RETRIES);
|
| 1469 | if (err)
|
| 1470 | printf("error %d %d\n", err, errno);
|
| 1471 | assert(err == 0);
|
| 1472 | }
|
| 1473 | }
|
| 1474 | }
|
| 1475 |
|
| 1476 | static void test_map_parallel(void)
|
| 1477 | {
|
| 1478 | int i, fd, key = 0, value = 0, j = 0;
|
| 1479 | int data[2];
|
| 1480 |
|
| 1481 | fd = bpf_map_create(BPF_MAP_TYPE_HASH, NULL, sizeof(key), sizeof(value),
|
| 1482 | MAP_SIZE, &map_opts);
|
| 1483 | if (fd < 0) {
|
| 1484 | printf("Failed to create map for parallel test '%s'!\n",
|
| 1485 | strerror(errno));
|
| 1486 | exit(1);
|
| 1487 | }
|
| 1488 |
|
| 1489 | again:
|
| 1490 | |
| 1491 | * child_0 adds key=0, key=1024, key=2048, ...
|
| 1492 | * child_1 adds key=1, key=1025, key=2049, ...
|
| 1493 | * child_1023 adds key=1023, ...
|
| 1494 |
|
| 1495 | data[0] = fd;
|
| 1496 | data[1] = DO_UPDATE;
|
| 1497 | run_parallel(TASKS, test_update_delete, data);
|
| 1498 |
|
| 1499 |
|
| 1500 | assert(bpf_map_update_elem(fd, &key, &value, BPF_NOEXIST) < 0 &&
|
| 1501 | errno == EEXIST);
|
| 1502 |
|
| 1503 |
|
| 1504 | assert(bpf_map_get_next_key(fd, NULL, &key) == 0);
|
| 1505 | key = -1;
|
| 1506 | for (i = 0; i < MAP_SIZE; i++)
|
| 1507 | assert(bpf_map_get_next_key(fd, &key, &key) == 0);
|
| 1508 | assert(bpf_map_get_next_key(fd, &key, &key) < 0 && errno == ENOENT);
|
| 1509 |
|
| 1510 |
|
| 1511 | for (i = 0; i < MAP_SIZE; i++) {
|
| 1512 | key = MAP_SIZE - i - 1;
|
| 1513 |
|
| 1514 | assert(bpf_map_lookup_elem(fd, &key, &value) == 0 &&
|
| 1515 | value == key);
|
| 1516 | }
|
| 1517 |
|
| 1518 |
|
| 1519 | data[1] = DO_DELETE;
|
| 1520 | run_parallel(TASKS, test_update_delete, data);
|
| 1521 |
|
| 1522 |
|
| 1523 | key = -1;
|
| 1524 | assert(bpf_map_get_next_key(fd, NULL, &key) < 0 && errno == ENOENT);
|
| 1525 | assert(bpf_map_get_next_key(fd, &key, &key) < 0 && errno == ENOENT);
|
| 1526 |
|
| 1527 | key = 0;
|
| 1528 | bpf_map_delete_elem(fd, &key);
|
| 1529 | if (j++ < 5)
|
| 1530 | goto again;
|
| 1531 | close(fd);
|
| 1532 | }
|
| 1533 |
|
| 1534 | static void test_map_rdonly(void)
|
| 1535 | {
|
| 1536 | int fd, key = 0, value = 0;
|
| 1537 | __u32 old_flags;
|
| 1538 |
|
| 1539 | old_flags = map_opts.map_flags;
|
| 1540 | map_opts.map_flags |= BPF_F_RDONLY;
|
| 1541 | fd = bpf_map_create(BPF_MAP_TYPE_HASH, NULL, sizeof(key), sizeof(value),
|
| 1542 | MAP_SIZE, &map_opts);
|
| 1543 | map_opts.map_flags = old_flags;
|
| 1544 | if (fd < 0) {
|
| 1545 | printf("Failed to create map for read only test '%s'!\n",
|
| 1546 | strerror(errno));
|
| 1547 | exit(1);
|
| 1548 | }
|
| 1549 |
|
| 1550 | key = 1;
|
| 1551 | value = 1234;
|
| 1552 |
|
| 1553 | assert(bpf_map_update_elem(fd, &key, &value, BPF_ANY) < 0 &&
|
| 1554 | errno == EPERM);
|
| 1555 |
|
| 1556 |
|
| 1557 | assert(bpf_map_lookup_elem(fd, &key, &value) < 0 && errno == ENOENT);
|
| 1558 | assert(bpf_map_get_next_key(fd, &key, &value) < 0 && errno == ENOENT);
|
| 1559 |
|
| 1560 | close(fd);
|
| 1561 | }
|
| 1562 |
|
| 1563 | static void test_map_wronly_hash(void)
|
| 1564 | {
|
| 1565 | int fd, key = 0, value = 0;
|
| 1566 | __u32 old_flags;
|
| 1567 |
|
| 1568 | old_flags = map_opts.map_flags;
|
| 1569 | map_opts.map_flags |= BPF_F_WRONLY;
|
| 1570 | fd = bpf_map_create(BPF_MAP_TYPE_HASH, NULL, sizeof(key), sizeof(value),
|
| 1571 | MAP_SIZE, &map_opts);
|
| 1572 | map_opts.map_flags = old_flags;
|
| 1573 | if (fd < 0) {
|
| 1574 | printf("Failed to create map for write only test '%s'!\n",
|
| 1575 | strerror(errno));
|
| 1576 | exit(1);
|
| 1577 | }
|
| 1578 |
|
| 1579 | key = 1;
|
| 1580 | value = 1234;
|
| 1581 |
|
| 1582 | assert(bpf_map_update_elem(fd, &key, &value, BPF_ANY) == 0);
|
| 1583 |
|
| 1584 |
|
| 1585 | assert(bpf_map_lookup_elem(fd, &key, &value) < 0 && errno == EPERM);
|
| 1586 | assert(bpf_map_get_next_key(fd, &key, &value) < 0 && errno == EPERM);
|
| 1587 |
|
| 1588 | close(fd);
|
| 1589 | }
|
| 1590 |
|
| 1591 | static void test_map_wronly_stack_or_queue(enum bpf_map_type map_type)
|
| 1592 | {
|
| 1593 | int fd, value = 0;
|
| 1594 | __u32 old_flags;
|
| 1595 |
|
| 1596 |
|
| 1597 | assert(map_type == BPF_MAP_TYPE_QUEUE ||
|
| 1598 | map_type == BPF_MAP_TYPE_STACK);
|
| 1599 | old_flags = map_opts.map_flags;
|
| 1600 | map_opts.map_flags |= BPF_F_WRONLY;
|
| 1601 | fd = bpf_map_create(map_type, NULL, 0, sizeof(value), MAP_SIZE, &map_opts);
|
| 1602 | map_opts.map_flags = old_flags;
|
| 1603 |
|
| 1604 | if (map_opts.map_flags & BPF_F_NO_PREALLOC) {
|
| 1605 | assert(fd < 0 && errno == EINVAL);
|
| 1606 | return;
|
| 1607 | }
|
| 1608 | if (fd < 0) {
|
| 1609 | printf("Failed to create map '%s'!\n", strerror(errno));
|
| 1610 | exit(1);
|
| 1611 | }
|
| 1612 |
|
| 1613 | value = 1234;
|
| 1614 | assert(bpf_map_update_elem(fd, NULL, &value, BPF_ANY) == 0);
|
| 1615 |
|
| 1616 |
|
| 1617 | assert(bpf_map_lookup_elem(fd, NULL, &value) < 0 && errno == EPERM);
|
| 1618 |
|
| 1619 |
|
| 1620 | assert(bpf_map_lookup_and_delete_elem(fd, NULL, &value) < 0 &&
|
| 1621 | errno == EPERM);
|
| 1622 |
|
| 1623 | close(fd);
|
| 1624 | }
|
| 1625 |
|
| 1626 | static void test_map_wronly(void)
|
| 1627 | {
|
| 1628 | test_map_wronly_hash();
|
| 1629 | test_map_wronly_stack_or_queue(BPF_MAP_TYPE_STACK);
|
| 1630 | test_map_wronly_stack_or_queue(BPF_MAP_TYPE_QUEUE);
|
| 1631 | }
|
| 1632 |
|
| 1633 | static void prepare_reuseport_grp(int type, int map_fd, size_t map_elem_size,
|
| 1634 | __s64 *fds64, __u64 *sk_cookies,
|
| 1635 | unsigned int n)
|
| 1636 | {
|
| 1637 | socklen_t optlen, addrlen;
|
| 1638 | struct sockaddr_in6 s6;
|
| 1639 | const __u32 index0 = 0;
|
| 1640 | const int optval = 1;
|
| 1641 | unsigned int i;
|
| 1642 | u64 sk_cookie;
|
| 1643 | void *value;
|
| 1644 | __s32 fd32;
|
| 1645 | __s64 fd64;
|
| 1646 | int err;
|
| 1647 |
|
| 1648 | s6.sin6_family = AF_INET6;
|
| 1649 | s6.sin6_addr = in6addr_any;
|
| 1650 | s6.sin6_port = 0;
|
| 1651 | addrlen = sizeof(s6);
|
| 1652 | optlen = sizeof(sk_cookie);
|
| 1653 |
|
| 1654 | for (i = 0; i < n; i++) {
|
| 1655 | fd64 = socket(AF_INET6, type, 0);
|
| 1656 | CHECK(fd64 == -1, "socket()",
|
| 1657 | "sock_type:%d fd64:%lld errno:%d\n",
|
| 1658 | type, fd64, errno);
|
| 1659 |
|
| 1660 | err = setsockopt(fd64, SOL_SOCKET, SO_REUSEPORT,
|
| 1661 | &optval, sizeof(optval));
|
| 1662 | CHECK(err == -1, "setsockopt(SO_REUSEPORT)",
|
| 1663 | "err:%d errno:%d\n", err, errno);
|
| 1664 |
|
| 1665 |
|
| 1666 | if (map_elem_size == sizeof(__u64))
|
| 1667 | value = &fd64;
|
| 1668 | else {
|
| 1669 | assert(map_elem_size == sizeof(__u32));
|
| 1670 | fd32 = (__s32)fd64;
|
| 1671 | value = &fd32;
|
| 1672 | }
|
| 1673 | err = bpf_map_update_elem(map_fd, &index0, value, BPF_ANY);
|
| 1674 | CHECK(err >= 0 || errno != EINVAL,
|
| 1675 | "reuseport array update unbound sk",
|
| 1676 | "sock_type:%d err:%d errno:%d\n",
|
| 1677 | type, err, errno);
|
| 1678 |
|
| 1679 | err = bind(fd64, (struct sockaddr *)&s6, sizeof(s6));
|
| 1680 | CHECK(err == -1, "bind()",
|
| 1681 | "sock_type:%d err:%d errno:%d\n", type, err, errno);
|
| 1682 |
|
| 1683 | if (i == 0) {
|
| 1684 | err = getsockname(fd64, (struct sockaddr *)&s6,
|
| 1685 | &addrlen);
|
| 1686 | CHECK(err == -1, "getsockname()",
|
| 1687 | "sock_type:%d err:%d errno:%d\n",
|
| 1688 | type, err, errno);
|
| 1689 | }
|
| 1690 |
|
| 1691 | err = getsockopt(fd64, SOL_SOCKET, SO_COOKIE, &sk_cookie,
|
| 1692 | &optlen);
|
| 1693 | CHECK(err == -1, "getsockopt(SO_COOKIE)",
|
| 1694 | "sock_type:%d err:%d errno:%d\n", type, err, errno);
|
| 1695 |
|
| 1696 | if (type == SOCK_STREAM) {
|
| 1697 | |
| 1698 | * reuseport_array does not allow
|
| 1699 | * non-listening tcp sk.
|
| 1700 |
|
| 1701 | err = bpf_map_update_elem(map_fd, &index0, value,
|
| 1702 | BPF_ANY);
|
| 1703 | CHECK(err >= 0 || errno != EINVAL,
|
| 1704 | "reuseport array update non-listening sk",
|
| 1705 | "sock_type:%d err:%d errno:%d\n",
|
| 1706 | type, err, errno);
|
| 1707 | err = listen(fd64, 0);
|
| 1708 | CHECK(err == -1, "listen()",
|
| 1709 | "sock_type:%d, err:%d errno:%d\n",
|
| 1710 | type, err, errno);
|
| 1711 | }
|
| 1712 |
|
| 1713 | fds64[i] = fd64;
|
| 1714 | sk_cookies[i] = sk_cookie;
|
| 1715 | }
|
| 1716 | }
|
| 1717 |
|
| 1718 | static void test_reuseport_array(void)
|
| 1719 | {
|
| 1720 | #define REUSEPORT_FD_IDX(err, last) ({ (err) ? last : !last; })
|
| 1721 |
|
| 1722 | const __u32 array_size = 4, index0 = 0, index3 = 3;
|
| 1723 | int types[2] = { SOCK_STREAM, SOCK_DGRAM }, type;
|
| 1724 | __u64 grpa_cookies[2], sk_cookie, map_cookie;
|
| 1725 | __s64 grpa_fds64[2] = { -1, -1 }, fd64 = -1;
|
| 1726 | const __u32 bad_index = array_size;
|
| 1727 | int map_fd, err, t, f;
|
| 1728 | __u32 fds_idx = 0;
|
| 1729 | int fd;
|
| 1730 |
|
| 1731 | map_fd = bpf_map_create(BPF_MAP_TYPE_REUSEPORT_SOCKARRAY, NULL,
|
| 1732 | sizeof(__u32), sizeof(__u64), array_size, NULL);
|
| 1733 | CHECK(map_fd < 0, "reuseport array create",
|
| 1734 | "map_fd:%d, errno:%d\n", map_fd, errno);
|
| 1735 |
|
| 1736 |
|
| 1737 | err = bpf_map_delete_elem(map_fd, &bad_index);
|
| 1738 | CHECK(err >= 0 || errno != E2BIG, "reuseport array del >=max_entries",
|
| 1739 | "err:%d errno:%d\n", err, errno);
|
| 1740 |
|
| 1741 | err = bpf_map_update_elem(map_fd, &bad_index, &fd64, BPF_ANY);
|
| 1742 | CHECK(err >= 0 || errno != E2BIG,
|
| 1743 | "reuseport array update >=max_entries",
|
| 1744 | "err:%d errno:%d\n", err, errno);
|
| 1745 |
|
| 1746 | err = bpf_map_lookup_elem(map_fd, &bad_index, &map_cookie);
|
| 1747 | CHECK(err >= 0 || errno != ENOENT,
|
| 1748 | "reuseport array update >=max_entries",
|
| 1749 | "err:%d errno:%d\n", err, errno);
|
| 1750 |
|
| 1751 |
|
| 1752 | err = bpf_map_lookup_elem(map_fd, &index3, &map_cookie);
|
| 1753 | CHECK(err >= 0 || errno != ENOENT,
|
| 1754 | "reuseport array lookup not-exist elem",
|
| 1755 | "err:%d errno:%d\n", err, errno);
|
| 1756 | err = bpf_map_delete_elem(map_fd, &index3);
|
| 1757 | CHECK(err >= 0 || errno != ENOENT,
|
| 1758 | "reuseport array del not-exist elem",
|
| 1759 | "err:%d errno:%d\n", err, errno);
|
| 1760 |
|
| 1761 | for (t = 0; t < ARRAY_SIZE(types); t++) {
|
| 1762 | type = types[t];
|
| 1763 |
|
| 1764 | prepare_reuseport_grp(type, map_fd, sizeof(__u64), grpa_fds64,
|
| 1765 | grpa_cookies, ARRAY_SIZE(grpa_fds64));
|
| 1766 |
|
| 1767 |
|
| 1768 |
|
| 1769 | err = bpf_map_update_elem(map_fd, &index3, &grpa_fds64[fds_idx],
|
| 1770 | BPF_EXIST);
|
| 1771 | CHECK(err >= 0 || errno != ENOENT,
|
| 1772 | "reuseport array update empty elem BPF_EXIST",
|
| 1773 | "sock_type:%d err:%d errno:%d\n",
|
| 1774 | type, err, errno);
|
| 1775 | fds_idx = REUSEPORT_FD_IDX(err, fds_idx);
|
| 1776 |
|
| 1777 |
|
| 1778 | err = bpf_map_update_elem(map_fd, &index3, &grpa_fds64[fds_idx],
|
| 1779 | BPF_NOEXIST);
|
| 1780 | CHECK(err < 0,
|
| 1781 | "reuseport array update empty elem BPF_NOEXIST",
|
| 1782 | "sock_type:%d err:%d errno:%d\n",
|
| 1783 | type, err, errno);
|
| 1784 | fds_idx = REUSEPORT_FD_IDX(err, fds_idx);
|
| 1785 |
|
| 1786 |
|
| 1787 | err = bpf_map_update_elem(map_fd, &index3, &grpa_fds64[fds_idx],
|
| 1788 | BPF_EXIST);
|
| 1789 | CHECK(err < 0,
|
| 1790 | "reuseport array update same elem BPF_EXIST",
|
| 1791 | "sock_type:%d err:%d errno:%d\n", type, err, errno);
|
| 1792 | fds_idx = REUSEPORT_FD_IDX(err, fds_idx);
|
| 1793 |
|
| 1794 |
|
| 1795 | err = bpf_map_update_elem(map_fd, &index3, &grpa_fds64[fds_idx],
|
| 1796 | BPF_NOEXIST);
|
| 1797 | CHECK(err >= 0 || errno != EEXIST,
|
| 1798 | "reuseport array update non-empty elem BPF_NOEXIST",
|
| 1799 | "sock_type:%d err:%d errno:%d\n",
|
| 1800 | type, err, errno);
|
| 1801 | fds_idx = REUSEPORT_FD_IDX(err, fds_idx);
|
| 1802 |
|
| 1803 |
|
| 1804 | err = bpf_map_update_elem(map_fd, &index3, &grpa_fds64[fds_idx],
|
| 1805 | BPF_ANY);
|
| 1806 | CHECK(err < 0,
|
| 1807 | "reuseport array update same sk with BPF_ANY",
|
| 1808 | "sock_type:%d err:%d errno:%d\n", type, err, errno);
|
| 1809 |
|
| 1810 | fd64 = grpa_fds64[fds_idx];
|
| 1811 | sk_cookie = grpa_cookies[fds_idx];
|
| 1812 |
|
| 1813 |
|
| 1814 | err = bpf_map_update_elem(map_fd, &index3, &fd64, BPF_ANY);
|
| 1815 | CHECK(err >= 0 || errno != EBUSY,
|
| 1816 | "reuseport array update same sk with same index",
|
| 1817 | "sock_type:%d err:%d errno:%d\n",
|
| 1818 | type, err, errno);
|
| 1819 |
|
| 1820 | err = bpf_map_update_elem(map_fd, &index0, &fd64, BPF_ANY);
|
| 1821 | CHECK(err >= 0 || errno != EBUSY,
|
| 1822 | "reuseport array update same sk with different index",
|
| 1823 | "sock_type:%d err:%d errno:%d\n",
|
| 1824 | type, err, errno);
|
| 1825 |
|
| 1826 |
|
| 1827 | err = bpf_map_delete_elem(map_fd, &index3);
|
| 1828 | CHECK(err < 0, "reuseport array delete sk",
|
| 1829 | "sock_type:%d err:%d errno:%d\n",
|
| 1830 | type, err, errno);
|
| 1831 |
|
| 1832 |
|
| 1833 | err = bpf_map_update_elem(map_fd, &index3, &fd64, BPF_NOEXIST);
|
| 1834 | CHECK(err < 0,
|
| 1835 | "reuseport array re-add with BPF_NOEXIST after del",
|
| 1836 | "sock_type:%d err:%d errno:%d\n", type, err, errno);
|
| 1837 |
|
| 1838 |
|
| 1839 | err = bpf_map_lookup_elem(map_fd, &index3, &map_cookie);
|
| 1840 | CHECK(err < 0 || sk_cookie != map_cookie,
|
| 1841 | "reuseport array lookup re-added sk",
|
| 1842 | "sock_type:%d err:%d errno:%d sk_cookie:0x%llx map_cookie:0x%llxn",
|
| 1843 | type, err, errno, sk_cookie, map_cookie);
|
| 1844 |
|
| 1845 |
|
| 1846 | for (f = 0; f < ARRAY_SIZE(grpa_fds64); f++)
|
| 1847 | close(grpa_fds64[f]);
|
| 1848 | err = bpf_map_lookup_elem(map_fd, &index3, &map_cookie);
|
| 1849 | CHECK(err >= 0 || errno != ENOENT,
|
| 1850 | "reuseport array lookup after close()",
|
| 1851 | "sock_type:%d err:%d errno:%d\n",
|
| 1852 | type, err, errno);
|
| 1853 | }
|
| 1854 |
|
| 1855 |
|
| 1856 | fd64 = socket(AF_INET6, SOCK_RAW, IPPROTO_UDP);
|
| 1857 | CHECK(fd64 == -1, "socket(SOCK_RAW)", "err:%d errno:%d\n",
|
| 1858 | err, errno);
|
| 1859 | err = bpf_map_update_elem(map_fd, &index3, &fd64, BPF_NOEXIST);
|
| 1860 | CHECK(err >= 0 || errno != ENOTSUPP, "reuseport array update SOCK_RAW",
|
| 1861 | "err:%d errno:%d\n", err, errno);
|
| 1862 | close(fd64);
|
| 1863 |
|
| 1864 |
|
| 1865 | close(map_fd);
|
| 1866 |
|
| 1867 |
|
| 1868 | map_fd = bpf_map_create(BPF_MAP_TYPE_REUSEPORT_SOCKARRAY, NULL,
|
| 1869 | sizeof(__u32), sizeof(__u32), array_size, NULL);
|
| 1870 | CHECK(map_fd < 0, "reuseport array create",
|
| 1871 | "map_fd:%d, errno:%d\n", map_fd, errno);
|
| 1872 | prepare_reuseport_grp(SOCK_STREAM, map_fd, sizeof(__u32), &fd64,
|
| 1873 | &sk_cookie, 1);
|
| 1874 | fd = fd64;
|
| 1875 | err = bpf_map_update_elem(map_fd, &index3, &fd, BPF_NOEXIST);
|
| 1876 | CHECK(err < 0, "reuseport array update 32 bit fd",
|
| 1877 | "err:%d errno:%d\n", err, errno);
|
| 1878 | err = bpf_map_lookup_elem(map_fd, &index3, &map_cookie);
|
| 1879 | CHECK(err >= 0 || errno != ENOSPC,
|
| 1880 | "reuseport array lookup 32 bit fd",
|
| 1881 | "err:%d errno:%d\n", err, errno);
|
| 1882 | close(fd);
|
| 1883 | close(map_fd);
|
| 1884 | }
|
| 1885 |
|
| 1886 | static void run_all_tests(void)
|
| 1887 | {
|
| 1888 | test_hashmap(0, NULL);
|
| 1889 | test_hashmap_percpu(0, NULL);
|
| 1890 | test_hashmap_walk(0, NULL);
|
| 1891 | test_hashmap_zero_seed();
|
| 1892 |
|
| 1893 | test_arraymap(0, NULL);
|
| 1894 | test_arraymap_percpu(0, NULL);
|
| 1895 |
|
| 1896 | test_arraymap_percpu_many_keys();
|
| 1897 |
|
| 1898 | test_devmap(0, NULL);
|
| 1899 | test_devmap_hash(0, NULL);
|
| 1900 | test_sockmap(0, NULL);
|
| 1901 |
|
| 1902 | test_map_large();
|
| 1903 | test_map_parallel();
|
| 1904 | test_map_stress();
|
| 1905 |
|
| 1906 | test_map_rdonly();
|
| 1907 | test_map_wronly();
|
| 1908 |
|
| 1909 | test_reuseport_array();
|
| 1910 |
|
| 1911 | test_queuemap(0, NULL);
|
| 1912 | test_stackmap(0, NULL);
|
| 1913 |
|
| 1914 | test_map_in_map();
|
| 1915 | }
|
| 1916 |
|
| 1917 | #define DEFINE_TEST(name) extern void test_##name(void);
|
| 1918 | #include <map_tests/tests.h>
|
| 1919 | #undef DEFINE_TEST
|
| 1920 |
|
| 1921 | int main(void)
|
| 1922 | {
|
| 1923 | srand(time(NULL));
|
| 1924 |
|
| 1925 | libbpf_set_strict_mode(LIBBPF_STRICT_ALL);
|
| 1926 |
|
| 1927 | map_opts.map_flags = 0;
|
| 1928 | run_all_tests();
|
| 1929 |
|
| 1930 | map_opts.map_flags = BPF_F_NO_PREALLOC;
|
| 1931 | run_all_tests();
|
| 1932 |
|
| 1933 | #define DEFINE_TEST(name) test_##name();
|
| 1934 | #include <map_tests/tests.h>
|
| 1935 | #undef DEFINE_TEST
|
| 1936 |
|
| 1937 | printf("test_maps: OK, %d SKIPPED\n", skips);
|
| 1938 | return 0;
|
| 1939 | }
|