value = 0; /* BPF_NOEXIST means add new element if it doesn't exist. */
assert(bpf_map_update_elem(fd, &key, &value, BPF_NOEXIST) < 0 && /* key=1 already exists. */
errno == EEXIST);
/* -1 is an invalid flag. */
assert(bpf_map_update_elem(fd, &key, &value, -1) < 0 &&
errno == EINVAL);
/* Check that key=1 can be found. */
assert(bpf_map_lookup_elem(fd, &key, &value) == 0 && value == 1234);
staticvoid test_hashmap_sizes(unsignedint task, void *data)
{ int fd, i, j;
for (i = 1; i <= 512; i <<= 1) for (j = 1; j <= 1 << 18; j <<= 1) {
fd = bpf_map_create(BPF_MAP_TYPE_HASH, NULL, i, j, 2, &map_opts); if (fd < 0) { if (errno == ENOMEM) return;
printf("Failed to create hashmap key=%d value=%d '%s'\n",
i, j, strerror(errno)); exit(1);
}
close(fd);
usleep(10); /* give kernel time to destroy */
}
}
/* Lookup and delete elem key=1 and check value. */
assert(bpf_map_lookup_and_delete_elem(fd, &key, value) == 0 &&
bpf_percpu(value,0) == 100);
for (i = 0; i < nr_cpus; i++)
bpf_percpu(value,i) = i + 100;
/* Insert key=1 element which should not exist. */
assert(bpf_map_update_elem(fd, &key, value, BPF_NOEXIST) == 0);
expected_key_mask |= key;
/* BPF_NOEXIST means add new element if it doesn't exist. */
assert(bpf_map_update_elem(fd, &key, value, BPF_NOEXIST) < 0 && /* key=1 already exists. */
errno == EEXIST);
/* -1 is an invalid flag. */
assert(bpf_map_update_elem(fd, &key, value, -1) < 0 &&
errno == EINVAL);
/* Check that key=1 can be found. Value could be 0 if the lookup * was run from a different CPU.
*/
bpf_percpu(value, 0) = 1;
assert(bpf_map_lookup_elem(fd, &key, value) == 0 &&
bpf_percpu(value, 0) == 100);
key = 2; /* Check that key=2 is not found. */
assert(bpf_map_lookup_elem(fd, &key, value) < 0 && errno == ENOENT);
/* BPF_EXIST means update existing element. */
assert(bpf_map_update_elem(fd, &key, value, BPF_EXIST) < 0 && /* key=2 is not there. */
errno == ENOENT);
staticvoid test_arraymap_percpu_many_keys(void)
{ unsignedint nr_cpus = bpf_num_possible_cpus();
BPF_DECLARE_PERCPU(long, values); /* nr_keys is not too large otherwise the test stresses percpu * allocator more than anything else
*/ unsignedint nr_keys = 2000; int key, fd, i;
fd = bpf_map_create(BPF_MAP_TYPE_QUEUE, NULL, 0, sizeof(val), MAP_SIZE, &map_opts); /* Queue map does not support BPF_F_NO_PREALLOC */ if (map_opts.map_flags & BPF_F_NO_PREALLOC) {
assert(fd < 0 && errno == EINVAL); return;
} if (fd < 0) {
printf("Failed to create queuemap '%s'!\n", strerror(errno)); exit(1);
}
/* Push MAP_SIZE elements */ for (i = 0; i < MAP_SIZE; i++)
assert(bpf_map_update_elem(fd, NULL, &vals[i], 0) == 0);
/* Check that element cannot be pushed due to max_entries limit */
assert(bpf_map_update_elem(fd, NULL, &val, 0) < 0 &&
errno == E2BIG);
/* Peek element */
assert(bpf_map_lookup_elem(fd, NULL, &val) == 0 && val == vals[0]);
/* Replace half elements */ for (i = MAP_SIZE; i < MAP_SIZE + MAP_SIZE/2; i++)
assert(bpf_map_update_elem(fd, NULL, &vals[i], BPF_EXIST) == 0);
/* Pop all elements */ for (i = MAP_SIZE/2; i < MAP_SIZE + MAP_SIZE/2; i++)
assert(bpf_map_lookup_and_delete_elem(fd, NULL, &val) == 0 &&
val == vals[i]);
/* Check that there are not elements left */
assert(bpf_map_lookup_and_delete_elem(fd, NULL, &val) < 0 &&
errno == ENOENT);
/* Check that non supported functions set errno to EINVAL */
assert(bpf_map_delete_elem(fd, NULL) < 0 && errno == EINVAL);
assert(bpf_map_get_next_key(fd, NULL, NULL) < 0 && errno == EINVAL);
close(fd);
}
staticvoid test_stackmap(unsignedint task, void *data)
{ constint MAP_SIZE = 32;
__u32 vals[MAP_SIZE + MAP_SIZE/2], val = 0; int fd, i;
/* Fill test values to be used */ for (i = 0; i < MAP_SIZE + MAP_SIZE/2; i++)
vals[i] = rand();
fd = bpf_map_create(BPF_MAP_TYPE_STACK, NULL, 0, sizeof(val), MAP_SIZE, &map_opts); /* Stack map does not support BPF_F_NO_PREALLOC */ if (map_opts.map_flags & BPF_F_NO_PREALLOC) {
assert(fd < 0 && errno == EINVAL); return;
} if (fd < 0) {
printf("Failed to create stackmap '%s'!\n", strerror(errno)); exit(1);
}
/* Push MAP_SIZE elements */ for (i = 0; i < MAP_SIZE; i++)
assert(bpf_map_update_elem(fd, NULL, &vals[i], 0) == 0);
/* Check that element cannot be pushed due to max_entries limit */
assert(bpf_map_update_elem(fd, NULL, &val, 0) < 0 &&
errno == E2BIG);
/* Peek element */
assert(bpf_map_lookup_elem(fd, NULL, &val) == 0 && val == vals[i - 1]);
/* Replace half elements */ for (i = MAP_SIZE; i < MAP_SIZE + MAP_SIZE/2; i++)
assert(bpf_map_update_elem(fd, NULL, &vals[i], BPF_EXIST) == 0);
/* Pop all elements */ for (i = MAP_SIZE + MAP_SIZE/2 - 1; i >= MAP_SIZE/2; i--)
assert(bpf_map_lookup_and_delete_elem(fd, NULL, &val) == 0 &&
val == vals[i]);
/* Check that there are not elements left */
assert(bpf_map_lookup_and_delete_elem(fd, NULL, &val) < 0 &&
errno == ENOENT);
/* Check that non supported functions set errno to EINVAL */
assert(bpf_map_delete_elem(fd, NULL) < 0 && errno == EINVAL);
assert(bpf_map_get_next_key(fd, NULL, NULL) < 0 && errno == EINVAL);
/* Create some sockets to use with sockmap */ for (i = 0; i < 2; i++) {
sfd[i] = socket(AF_INET, SOCK_STREAM, 0); if (sfd[i] < 0) goto out;
err = setsockopt(sfd[i], SOL_SOCKET, SO_REUSEADDR,
(char *)&one, sizeof(one)); if (err) {
printf("failed to setsockopt\n"); goto out;
}
err = ioctl(sfd[i], FIONBIO, (char *)&one); if (err < 0) {
printf("failed to ioctl\n"); goto out;
}
memset(&addr, 0, sizeof(struct sockaddr_in));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
addr.sin_port = htons(ports[i]);
err = bind(sfd[i], (struct sockaddr *)&addr, sizeof(addr)); if (err < 0) {
printf("failed to bind: err %i: %i:%i\n",
err, i, sfd[i]); goto out;
}
err = listen(sfd[i], 32); if (err < 0) {
printf("failed to listen\n"); goto out;
}
}
for (i = 2; i < 4; i++) {
sfd[i] = socket(AF_INET, SOCK_STREAM, 0); if (sfd[i] < 0) goto out;
err = setsockopt(sfd[i], SOL_SOCKET, SO_REUSEADDR,
(char *)&one, sizeof(one)); if (err) {
printf("set sock opt\n"); goto out;
}
memset(&addr, 0, sizeof(struct sockaddr_in));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
addr.sin_port = htons(ports[i - 2]);
err = connect(sfd[i], (struct sockaddr *)&addr, sizeof(addr)); if (err) {
printf("failed to connect\n"); goto out;
}
}
for (i = 4; i < 6; i++) {
sfd[i] = accept(sfd[i - 4], NULL, NULL); if (sfd[i] < 0) {
printf("accept failed\n"); goto out;
}
}
/* Test sockmap with connected sockets */
fd = bpf_map_create(BPF_MAP_TYPE_SOCKMAP, NULL, sizeof(key), sizeof(value),
6, NULL); if (fd < 0) { if (!libbpf_probe_bpf_map_type(BPF_MAP_TYPE_SOCKMAP, NULL)) {
printf("%s SKIP (unsupported map type BPF_MAP_TYPE_SOCKMAP)\n",
__func__);
skips++; for (i = 0; i < 6; i++)
close(sfd[i]); return;
}
printf("Failed to create sockmap %i\n", fd); goto out_sockmap;
}
/* Test update with unsupported UDP socket */
udp = socket(AF_INET, SOCK_DGRAM, 0);
i = 0;
err = bpf_map_update_elem(fd, &i, &udp, BPF_ANY); if (err) {
printf("Failed socket update SOCK_DGRAM '%i:%i'\n",
i, udp); goto out_sockmap;
}
close(udp);
/* Test update without programs */ for (i = 0; i < 6; i++) {
err = bpf_map_update_elem(fd, &i, &sfd[i], BPF_ANY); if (err) {
printf("Failed noprog update sockmap '%i:%i'\n",
i, sfd[i]); goto out_sockmap;
}
}
/* Test attaching/detaching bad fds */
err = bpf_prog_attach(-1, fd, BPF_SK_SKB_STREAM_PARSER, 0); if (!err) {
printf("Failed invalid parser prog attach\n"); goto out_sockmap;
}
/* Negative null entry lookup from datapath should be dropped */
buf[0] = 1;
buf[1] = 12;
sc = send(sfd[2], buf, 20, 0); if (sc < 0) {
printf("Failed sockmap send\n"); goto out_sockmap;
}
/* Push fd into same slot */
i = 2;
err = bpf_map_update_elem(fd, &i, &sfd[i], BPF_NOEXIST); if (!err) {
printf("Failed allowed sockmap dup slot BPF_NOEXIST\n"); goto out_sockmap;
}
err = bpf_map_update_elem(fd, &i, &sfd[i], BPF_ANY); if (err) {
printf("Failed sockmap update new slot BPF_ANY\n"); goto out_sockmap;
}
err = bpf_map_update_elem(fd, &i, &sfd[i], BPF_EXIST); if (err) {
printf("Failed sockmap update new slot BPF_EXIST\n"); goto out_sockmap;
}
/* Delete the elems without programs */ for (i = 2; i < 6; i++) {
err = bpf_map_delete_elem(fd, &i); if (err) {
printf("Failed delete sockmap %i '%i:%i'\n",
err, i, sfd[i]);
}
}
/* Test having multiple maps open and set with programs on same fds */
err = bpf_prog_attach(parse_prog, fd,
BPF_SK_SKB_STREAM_PARSER, 0); if (err) {
printf("Failed fd bpf parse prog attach\n"); goto out_sockmap;
}
err = bpf_prog_attach(verdict_prog, fd,
BPF_SK_SKB_STREAM_VERDICT, 0); if (err) {
printf("Failed fd bpf verdict prog attach\n"); goto out_sockmap;
}
for (i = 4; i < 6; i++) {
err = bpf_map_update_elem(fd, &i, &sfd[i], BPF_ANY); if (!err) {
printf("Failed allowed duplicate programs in update ANY sockmap %i '%i:%i'\n",
err, i, sfd[i]); goto out_sockmap;
}
err = bpf_map_update_elem(fd, &i, &sfd[i], BPF_NOEXIST); if (!err) {
printf("Failed allowed duplicate program in update NOEXIST sockmap %i '%i:%i'\n",
err, i, sfd[i]); goto out_sockmap;
}
err = bpf_map_update_elem(fd, &i, &sfd[i], BPF_EXIST); if (!err) {
printf("Failed allowed duplicate program in update EXIST sockmap %i '%i:%i'\n",
err, i, sfd[i]); goto out_sockmap;
}
}
/* Test tasks number of forked operations */ for (i = 0; i < tasks; i++) {
pid[i] = fork(); if (pid[i] == 0) { for (i = 0; i < 6; i++) {
bpf_map_delete_elem(map_fd_tx, &i);
bpf_map_delete_elem(map_fd_rx, &i);
bpf_map_update_elem(map_fd_tx, &i,
&sfd[i], BPF_ANY);
bpf_map_update_elem(map_fd_rx, &i,
&sfd[i], BPF_ANY);
} exit(0);
} elseif (pid[i] == -1) {
printf("Couldn't spawn #%d process!\n", i); exit(1);
}
}
/* Test map close sockets and empty maps */ for (i = 0; i < 6; i++) {
bpf_map_delete_elem(map_fd_tx, &i);
bpf_map_delete_elem(map_fd_rx, &i);
close(sfd[i]);
}
close(fd);
close(map_fd_rx);
bpf_object__close(parse_obj);
bpf_object__close(msg_obj);
bpf_object__close(verdict_obj); return;
out: for (i = 0; i < 6; i++)
close(sfd[i]);
printf("Failed to create sockmap '%i:%s'!\n", i, strerror(errno)); exit(1);
out_sockmap: for (i = 0; i < 6; i++) { if (map_fd_tx)
bpf_map_delete_elem(map_fd_tx, &i); if (map_fd_rx)
bpf_map_delete_elem(map_fd_rx, &i);
close(sfd[i]);
}
close(fd); exit(1);
}
#define MAPINMAP_PROG "./test_map_in_map.bpf.o" #define MAPINMAP_INVALID_PROG "./test_map_in_map_invalid.bpf.o" staticvoid test_map_in_map(void)
{ struct bpf_object *obj; struct bpf_map *map; int mim_fd, fd, err; int pos = 0; struct bpf_map_info info = {};
__u32 len = sizeof(info);
__u32 id = 0;
libbpf_print_fn_t old_print_fn;
map = bpf_object__find_map_by_name(obj, "mim_array"); if (!map) {
printf("Failed to load array of maps from test prog\n"); goto out_map_in_map;
}
err = bpf_map__set_inner_map_fd(map, fd); if (err) {
printf("Failed to set inner_map_fd for array of maps\n"); goto out_map_in_map;
}
map = bpf_object__find_map_by_name(obj, "mim_hash"); if (!map) {
printf("Failed to load hash of maps from test prog\n"); goto out_map_in_map;
}
err = bpf_map__set_inner_map_fd(map, fd); if (err) {
printf("Failed to set inner_map_fd for hash of maps\n"); goto out_map_in_map;
}
err = bpf_object__load(obj); if (err) {
printf("Failed to load test prog\n"); goto out_map_in_map;
}
map = bpf_object__find_map_by_name(obj, "mim_array"); if (!map) {
printf("Failed to load array of maps from test prog\n"); goto out_map_in_map;
}
mim_fd = bpf_map__fd(map); if (mim_fd < 0) {
printf("Failed to get descriptor for array of maps\n"); goto out_map_in_map;
}
err = bpf_map_update_elem(mim_fd, &pos, &fd, 0); if (err) {
printf("Failed to update array of maps\n"); goto out_map_in_map;
}
map = bpf_object__find_map_by_name(obj, "mim_hash"); if (!map) {
printf("Failed to load hash of maps from test prog\n"); goto out_map_in_map;
}
mim_fd = bpf_map__fd(map); if (mim_fd < 0) {
printf("Failed to get descriptor for hash of maps\n"); goto out_map_in_map;
}
err = bpf_map_update_elem(mim_fd, &pos, &fd, 0); if (err) {
printf("Failed to update hash of maps\n"); goto out_map_in_map;
}
close(fd);
fd = -1;
bpf_object__close(obj);
/* Test that failing bpf_object__create_map() destroys the inner map */
obj = bpf_object__open(MAPINMAP_INVALID_PROG);
err = libbpf_get_error(obj); if (err) {
printf("Failed to load %s program: %d %d",
MAPINMAP_INVALID_PROG, err, errno); goto out_map_in_map;
}
map = bpf_object__find_map_by_name(obj, "mim"); if (!map) {
printf("Failed to load array of maps from test prog\n"); goto out_map_in_map;
}
old_print_fn = libbpf_set_print(NULL);
err = bpf_object__load(obj); if (!err) {
printf("Loading obj supposed to fail\n"); goto out_map_in_map;
}
libbpf_set_print(old_print_fn);
/* Iterate over all maps to check whether the internal map * ("mim.internal") has been destroyed.
*/ while (true) {
err = bpf_map_get_next_id(id, &id); if (err) { if (errno == ENOENT) break;
printf("Failed to get next map: %d", errno); goto out_map_in_map;
}
fd = bpf_map_get_fd_by_id(id); if (fd < 0) { if (errno == ENOENT) continue;
printf("Failed to get map by id %u: %d", id, errno); goto out_map_in_map;
}
err = bpf_map_get_info_by_fd(fd, &info, &len); if (err) {
printf("Failed to get map info by fd %d: %d", fd,
errno); goto out_map_in_map;
}
if (!strcmp(info.name, "mim.inner")) {
printf("Inner map mim.inner was not destroyed\n"); goto out_map_in_map;
}
close(fd);
}
bpf_object__close(obj); return;
out_map_in_map: if (fd >= 0)
close(fd); exit(1);
}
#define MAP_SIZE (32 * 1024)
staticvoid test_map_large(void)
{
struct bigkey { int a; char b[4096]; longlong c;
} key; int fd, i, value;
fd = bpf_map_create(BPF_MAP_TYPE_HASH, NULL, sizeof(key), sizeof(value),
MAP_SIZE, &map_opts); if (fd < 0) {
printf("Failed to create large map '%s'!\n", strerror(errno)); exit(1);
}
for (i = 0; i < MAP_SIZE; i++) {
key = (struct bigkey) { .c = i };
value = i;
int map_update_retriable(int map_fd, constvoid *key, constvoid *value, int flags, int attempts,
retry_for_error_fn need_retry)
{ int delay = rand() % MIN_DELAY_RANGE_US;
while (bpf_map_update_elem(map_fd, key, value, flags)) { if (!attempts || !need_retry(errno)) return -errno;
if (delay <= MAX_DELAY_US / 2)
delay *= 2;
usleep(delay);
attempts--;
}
return 0;
}
staticint map_delete_retriable(int map_fd, constvoid *key, int attempts)
{ int delay = rand() % MIN_DELAY_RANGE_US;
while (bpf_map_delete_elem(map_fd, key)) { if (!attempts || (errno != EAGAIN && errno != EBUSY)) return -errno;
if (delay <= MAX_DELAY_US / 2)
delay *= 2;
usleep(delay);
attempts--;
}
return 0;
}
staticvoid test_update_delete(unsignedint fn, void *data)
{ int do_update = ((int *)data)[1]; int fd = ((int *)data)[0]; int i, key, value, err;
if (fn & 1)
test_hashmap_walk(fn, NULL); for (i = fn; i < MAP_SIZE; i += TASKS) {
key = value = i;
staticvoid test_map_parallel(void)
{ int i, fd, key = 0, value = 0, j = 0; int data[2];
fd = bpf_map_create(BPF_MAP_TYPE_HASH, NULL, sizeof(key), sizeof(value),
MAP_SIZE, &map_opts); if (fd < 0) {
printf("Failed to create map for parallel test '%s'!\n",
strerror(errno)); exit(1);
}
again: /* Use the same fd in children to add elements to this map: * child_0 adds key=0, key=1024, key=2048, ... * child_1 adds key=1, key=1025, key=2049, ... * child_1023 adds key=1023, ...
*/
data[0] = fd;
data[1] = DO_UPDATE;
run_parallel(TASKS, test_update_delete, data);
/* Check that key=0 is already there. */
assert(bpf_map_update_elem(fd, &key, &value, BPF_NOEXIST) < 0 &&
errno == EEXIST);
/* Check that all elements were inserted. */
assert(bpf_map_get_next_key(fd, NULL, &key) == 0);
key = -1; for (i = 0; i < MAP_SIZE; i++)
assert(bpf_map_get_next_key(fd, &key, &key) == 0);
assert(bpf_map_get_next_key(fd, &key, &key) < 0 && errno == ENOENT);
/* Another check for all elements */ for (i = 0; i < MAP_SIZE; i++) {
key = MAP_SIZE - i - 1;
assert(bpf_map_lookup_elem(fd, &key, &value) == 0 &&
value == key);
}
/* Now let's delete all elements in parallel. */
data[1] = DO_DELETE;
run_parallel(TASKS, test_update_delete, data);
/* Check that reading elements and keys from the map is not allowed. */
assert(bpf_map_lookup_elem(fd, &key, &value) < 0 && errno == EPERM);
assert(bpf_map_get_next_key(fd, &key, &value) < 0 && errno == EPERM);
close(fd);
}
staticvoid test_map_wronly_stack_or_queue(enum bpf_map_type map_type)
{ int fd, value = 0;
__u32 old_flags;
assert(map_type == BPF_MAP_TYPE_QUEUE ||
map_type == BPF_MAP_TYPE_STACK);
old_flags = map_opts.map_flags;
map_opts.map_flags |= BPF_F_WRONLY;
fd = bpf_map_create(map_type, NULL, 0, sizeof(value), MAP_SIZE, &map_opts);
map_opts.map_flags = old_flags; /* Stack/Queue maps do not support BPF_F_NO_PREALLOC */ if (map_opts.map_flags & BPF_F_NO_PREALLOC) {
assert(fd < 0 && errno == EINVAL); return;
} if (fd < 0) {
printf("Failed to create map '%s'!\n", strerror(errno)); exit(1);
}
value = 1234;
assert(bpf_map_update_elem(fd, NULL, &value, BPF_ANY) == 0);
/* Peek element should fail */
assert(bpf_map_lookup_elem(fd, NULL, &value) < 0 && errno == EPERM);
/* Pop element should fail */
assert(bpf_map_lookup_and_delete_elem(fd, NULL, &value) < 0 &&
errno == EPERM);
/* The same sk cannot be added to reuseport_array twice */
err = bpf_map_update_elem(map_fd, &index3, &fd64, BPF_ANY);
CHECK(err >= 0 || errno != EBUSY, "reuseport array update same sk with same index", "sock_type:%d err:%d errno:%d\n",
type, err, errno);
err = bpf_map_update_elem(map_fd, &index0, &fd64, BPF_ANY);
CHECK(err >= 0 || errno != EBUSY, "reuseport array update same sk with different index", "sock_type:%d err:%d errno:%d\n",
type, err, errno);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.