bpf: test BPF_MAP_TYPE_REUSEPORT_SOCKARRAY
This patch adds tests for the new BPF_MAP_TYPE_REUSEPORT_SOCKARRAY. Signed-off-by: Martin KaFai Lau <kafai@fb.com> Acked-by: Alexei Starovoitov <ast@kernel.org> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
This commit is contained in:
parent
3bd43a8c91
commit
6bc8529c41
2 changed files with 262 additions and 1 deletions
|
@ -1501,6 +1501,7 @@ static bool bpf_prog_type__needs_kver(enum bpf_prog_type type)
|
||||||
case BPF_PROG_TYPE_SK_MSG:
|
case BPF_PROG_TYPE_SK_MSG:
|
||||||
case BPF_PROG_TYPE_CGROUP_SOCK_ADDR:
|
case BPF_PROG_TYPE_CGROUP_SOCK_ADDR:
|
||||||
case BPF_PROG_TYPE_LIRC_MODE2:
|
case BPF_PROG_TYPE_LIRC_MODE2:
|
||||||
|
case BPF_PROG_TYPE_SK_REUSEPORT:
|
||||||
return false;
|
return false;
|
||||||
case BPF_PROG_TYPE_UNSPEC:
|
case BPF_PROG_TYPE_UNSPEC:
|
||||||
case BPF_PROG_TYPE_KPROBE:
|
case BPF_PROG_TYPE_KPROBE:
|
||||||
|
|
|
@ -17,7 +17,8 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
#include <linux/bpf.h>
|
#include <linux/bpf.h>
|
||||||
|
|
||||||
#include <bpf/bpf.h>
|
#include <bpf/bpf.h>
|
||||||
|
@ -26,8 +27,21 @@
|
||||||
#include "bpf_util.h"
|
#include "bpf_util.h"
|
||||||
#include "bpf_rlimit.h"
|
#include "bpf_rlimit.h"
|
||||||
|
|
||||||
|
#ifndef ENOTSUPP
|
||||||
|
#define ENOTSUPP 524
|
||||||
|
#endif
|
||||||
|
|
||||||
static int map_flags;
|
static int map_flags;
|
||||||
|
|
||||||
|
#define CHECK(condition, tag, format...) ({ \
|
||||||
|
int __ret = !!(condition); \
|
||||||
|
if (__ret) { \
|
||||||
|
printf("%s(%d):FAIL:%s ", __func__, __LINE__, tag); \
|
||||||
|
printf(format); \
|
||||||
|
exit(-1); \
|
||||||
|
} \
|
||||||
|
})
|
||||||
|
|
||||||
static void test_hashmap(int task, void *data)
|
static void test_hashmap(int task, void *data)
|
||||||
{
|
{
|
||||||
long long key, next_key, first_key, value;
|
long long key, next_key, first_key, value;
|
||||||
|
@ -1150,6 +1164,250 @@ static void test_map_wronly(void)
|
||||||
assert(bpf_map_get_next_key(fd, &key, &value) == -1 && errno == EPERM);
|
assert(bpf_map_get_next_key(fd, &key, &value) == -1 && errno == EPERM);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void prepare_reuseport_grp(int type, int map_fd,
|
||||||
|
__s64 *fds64, __u64 *sk_cookies,
|
||||||
|
unsigned int n)
|
||||||
|
{
|
||||||
|
socklen_t optlen, addrlen;
|
||||||
|
struct sockaddr_in6 s6;
|
||||||
|
const __u32 index0 = 0;
|
||||||
|
const int optval = 1;
|
||||||
|
unsigned int i;
|
||||||
|
u64 sk_cookie;
|
||||||
|
__s64 fd64;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
s6.sin6_family = AF_INET6;
|
||||||
|
s6.sin6_addr = in6addr_any;
|
||||||
|
s6.sin6_port = 0;
|
||||||
|
addrlen = sizeof(s6);
|
||||||
|
optlen = sizeof(sk_cookie);
|
||||||
|
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
fd64 = socket(AF_INET6, type, 0);
|
||||||
|
CHECK(fd64 == -1, "socket()",
|
||||||
|
"sock_type:%d fd64:%lld errno:%d\n",
|
||||||
|
type, fd64, errno);
|
||||||
|
|
||||||
|
err = setsockopt(fd64, SOL_SOCKET, SO_REUSEPORT,
|
||||||
|
&optval, sizeof(optval));
|
||||||
|
CHECK(err == -1, "setsockopt(SO_REUSEEPORT)",
|
||||||
|
"err:%d errno:%d\n", err, errno);
|
||||||
|
|
||||||
|
/* reuseport_array does not allow unbound sk */
|
||||||
|
err = bpf_map_update_elem(map_fd, &index0, &fd64,
|
||||||
|
BPF_ANY);
|
||||||
|
CHECK(err != -1 || errno != EINVAL,
|
||||||
|
"reuseport array update unbound sk",
|
||||||
|
"sock_type:%d err:%d errno:%d\n",
|
||||||
|
type, err, errno);
|
||||||
|
|
||||||
|
err = bind(fd64, (struct sockaddr *)&s6, sizeof(s6));
|
||||||
|
CHECK(err == -1, "bind()",
|
||||||
|
"sock_type:%d err:%d errno:%d\n", type, err, errno);
|
||||||
|
|
||||||
|
if (i == 0) {
|
||||||
|
err = getsockname(fd64, (struct sockaddr *)&s6,
|
||||||
|
&addrlen);
|
||||||
|
CHECK(err == -1, "getsockname()",
|
||||||
|
"sock_type:%d err:%d errno:%d\n",
|
||||||
|
type, err, errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
err = getsockopt(fd64, SOL_SOCKET, SO_COOKIE, &sk_cookie,
|
||||||
|
&optlen);
|
||||||
|
CHECK(err == -1, "getsockopt(SO_COOKIE)",
|
||||||
|
"sock_type:%d err:%d errno:%d\n", type, err, errno);
|
||||||
|
|
||||||
|
if (type == SOCK_STREAM) {
|
||||||
|
/*
|
||||||
|
* reuseport_array does not allow
|
||||||
|
* non-listening tcp sk.
|
||||||
|
*/
|
||||||
|
err = bpf_map_update_elem(map_fd, &index0, &fd64,
|
||||||
|
BPF_ANY);
|
||||||
|
CHECK(err != -1 || errno != EINVAL,
|
||||||
|
"reuseport array update non-listening sk",
|
||||||
|
"sock_type:%d err:%d errno:%d\n",
|
||||||
|
type, err, errno);
|
||||||
|
err = listen(fd64, 0);
|
||||||
|
CHECK(err == -1, "listen()",
|
||||||
|
"sock_type:%d, err:%d errno:%d\n",
|
||||||
|
type, err, errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
fds64[i] = fd64;
|
||||||
|
sk_cookies[i] = sk_cookie;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_reuseport_array(void)
|
||||||
|
{
|
||||||
|
#define REUSEPORT_FD_IDX(err, last) ({ (err) ? last : !last; })
|
||||||
|
|
||||||
|
const __u32 array_size = 4, index0 = 0, index3 = 3;
|
||||||
|
int types[2] = { SOCK_STREAM, SOCK_DGRAM }, type;
|
||||||
|
__u64 grpa_cookies[2], sk_cookie, map_cookie;
|
||||||
|
__s64 grpa_fds64[2] = { -1, -1 }, fd64 = -1;
|
||||||
|
const __u32 bad_index = array_size;
|
||||||
|
int map_fd, err, t, f;
|
||||||
|
__u32 fds_idx = 0;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
map_fd = bpf_create_map(BPF_MAP_TYPE_REUSEPORT_SOCKARRAY,
|
||||||
|
sizeof(__u32), sizeof(__u64), array_size, 0);
|
||||||
|
CHECK(map_fd == -1, "reuseport array create",
|
||||||
|
"map_fd:%d, errno:%d\n", map_fd, errno);
|
||||||
|
|
||||||
|
/* Test lookup/update/delete with invalid index */
|
||||||
|
err = bpf_map_delete_elem(map_fd, &bad_index);
|
||||||
|
CHECK(err != -1 || errno != E2BIG, "reuseport array del >=max_entries",
|
||||||
|
"err:%d errno:%d\n", err, errno);
|
||||||
|
|
||||||
|
err = bpf_map_update_elem(map_fd, &bad_index, &fd64, BPF_ANY);
|
||||||
|
CHECK(err != -1 || errno != E2BIG,
|
||||||
|
"reuseport array update >=max_entries",
|
||||||
|
"err:%d errno:%d\n", err, errno);
|
||||||
|
|
||||||
|
err = bpf_map_lookup_elem(map_fd, &bad_index, &map_cookie);
|
||||||
|
CHECK(err != -1 || errno != ENOENT,
|
||||||
|
"reuseport array update >=max_entries",
|
||||||
|
"err:%d errno:%d\n", err, errno);
|
||||||
|
|
||||||
|
/* Test lookup/delete non existence elem */
|
||||||
|
err = bpf_map_lookup_elem(map_fd, &index3, &map_cookie);
|
||||||
|
CHECK(err != -1 || errno != ENOENT,
|
||||||
|
"reuseport array lookup not-exist elem",
|
||||||
|
"err:%d errno:%d\n", err, errno);
|
||||||
|
err = bpf_map_delete_elem(map_fd, &index3);
|
||||||
|
CHECK(err != -1 || errno != ENOENT,
|
||||||
|
"reuseport array del not-exist elem",
|
||||||
|
"err:%d errno:%d\n", err, errno);
|
||||||
|
|
||||||
|
for (t = 0; t < ARRAY_SIZE(types); t++) {
|
||||||
|
type = types[t];
|
||||||
|
|
||||||
|
prepare_reuseport_grp(type, map_fd, grpa_fds64,
|
||||||
|
grpa_cookies, ARRAY_SIZE(grpa_fds64));
|
||||||
|
|
||||||
|
/* Test BPF_* update flags */
|
||||||
|
/* BPF_EXIST failure case */
|
||||||
|
err = bpf_map_update_elem(map_fd, &index3, &grpa_fds64[fds_idx],
|
||||||
|
BPF_EXIST);
|
||||||
|
CHECK(err != -1 || errno != ENOENT,
|
||||||
|
"reuseport array update empty elem BPF_EXIST",
|
||||||
|
"sock_type:%d err:%d errno:%d\n",
|
||||||
|
type, err, errno);
|
||||||
|
fds_idx = REUSEPORT_FD_IDX(err, fds_idx);
|
||||||
|
|
||||||
|
/* BPF_NOEXIST success case */
|
||||||
|
err = bpf_map_update_elem(map_fd, &index3, &grpa_fds64[fds_idx],
|
||||||
|
BPF_NOEXIST);
|
||||||
|
CHECK(err == -1,
|
||||||
|
"reuseport array update empty elem BPF_NOEXIST",
|
||||||
|
"sock_type:%d err:%d errno:%d\n",
|
||||||
|
type, err, errno);
|
||||||
|
fds_idx = REUSEPORT_FD_IDX(err, fds_idx);
|
||||||
|
|
||||||
|
/* BPF_EXIST success case. */
|
||||||
|
err = bpf_map_update_elem(map_fd, &index3, &grpa_fds64[fds_idx],
|
||||||
|
BPF_EXIST);
|
||||||
|
CHECK(err == -1,
|
||||||
|
"reuseport array update same elem BPF_EXIST",
|
||||||
|
"sock_type:%d err:%d errno:%d\n", type, err, errno);
|
||||||
|
fds_idx = REUSEPORT_FD_IDX(err, fds_idx);
|
||||||
|
|
||||||
|
/* BPF_NOEXIST failure case */
|
||||||
|
err = bpf_map_update_elem(map_fd, &index3, &grpa_fds64[fds_idx],
|
||||||
|
BPF_NOEXIST);
|
||||||
|
CHECK(err != -1 || errno != EEXIST,
|
||||||
|
"reuseport array update non-empty elem BPF_NOEXIST",
|
||||||
|
"sock_type:%d err:%d errno:%d\n",
|
||||||
|
type, err, errno);
|
||||||
|
fds_idx = REUSEPORT_FD_IDX(err, fds_idx);
|
||||||
|
|
||||||
|
/* BPF_ANY case (always succeed) */
|
||||||
|
err = bpf_map_update_elem(map_fd, &index3, &grpa_fds64[fds_idx],
|
||||||
|
BPF_ANY);
|
||||||
|
CHECK(err == -1,
|
||||||
|
"reuseport array update same sk with BPF_ANY",
|
||||||
|
"sock_type:%d err:%d errno:%d\n", type, err, errno);
|
||||||
|
|
||||||
|
fd64 = grpa_fds64[fds_idx];
|
||||||
|
sk_cookie = grpa_cookies[fds_idx];
|
||||||
|
|
||||||
|
/* The same sk cannot be added to reuseport_array twice */
|
||||||
|
err = bpf_map_update_elem(map_fd, &index3, &fd64, BPF_ANY);
|
||||||
|
CHECK(err != -1 || 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 != -1 || errno != EBUSY,
|
||||||
|
"reuseport array update same sk with different index",
|
||||||
|
"sock_type:%d err:%d errno:%d\n",
|
||||||
|
type, err, errno);
|
||||||
|
|
||||||
|
/* Test delete elem */
|
||||||
|
err = bpf_map_delete_elem(map_fd, &index3);
|
||||||
|
CHECK(err == -1, "reuseport array delete sk",
|
||||||
|
"sock_type:%d err:%d errno:%d\n",
|
||||||
|
type, err, errno);
|
||||||
|
|
||||||
|
/* Add it back with BPF_NOEXIST */
|
||||||
|
err = bpf_map_update_elem(map_fd, &index3, &fd64, BPF_NOEXIST);
|
||||||
|
CHECK(err == -1,
|
||||||
|
"reuseport array re-add with BPF_NOEXIST after del",
|
||||||
|
"sock_type:%d err:%d errno:%d\n", type, err, errno);
|
||||||
|
|
||||||
|
/* Test cookie */
|
||||||
|
err = bpf_map_lookup_elem(map_fd, &index3, &map_cookie);
|
||||||
|
CHECK(err == -1 || sk_cookie != map_cookie,
|
||||||
|
"reuseport array lookup re-added sk",
|
||||||
|
"sock_type:%d err:%d errno:%d sk_cookie:0x%llx map_cookie:0x%llxn",
|
||||||
|
type, err, errno, sk_cookie, map_cookie);
|
||||||
|
|
||||||
|
/* Test elem removed by close() */
|
||||||
|
for (f = 0; f < ARRAY_SIZE(grpa_fds64); f++)
|
||||||
|
close(grpa_fds64[f]);
|
||||||
|
err = bpf_map_lookup_elem(map_fd, &index3, &map_cookie);
|
||||||
|
CHECK(err != -1 || errno != ENOENT,
|
||||||
|
"reuseport array lookup after close()",
|
||||||
|
"sock_type:%d err:%d errno:%d\n",
|
||||||
|
type, err, errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test SOCK_RAW */
|
||||||
|
fd64 = socket(AF_INET6, SOCK_RAW, IPPROTO_UDP);
|
||||||
|
CHECK(fd64 == -1, "socket(SOCK_RAW)", "err:%d errno:%d\n",
|
||||||
|
err, errno);
|
||||||
|
err = bpf_map_update_elem(map_fd, &index3, &fd64, BPF_NOEXIST);
|
||||||
|
CHECK(err != -1 || errno != ENOTSUPP, "reuseport array update SOCK_RAW",
|
||||||
|
"err:%d errno:%d\n", err, errno);
|
||||||
|
close(fd64);
|
||||||
|
|
||||||
|
/* Close the 64 bit value map */
|
||||||
|
close(map_fd);
|
||||||
|
|
||||||
|
/* Test 32 bit fd */
|
||||||
|
map_fd = bpf_create_map(BPF_MAP_TYPE_REUSEPORT_SOCKARRAY,
|
||||||
|
sizeof(__u32), sizeof(__u32), array_size, 0);
|
||||||
|
CHECK(map_fd == -1, "reuseport array create",
|
||||||
|
"map_fd:%d, errno:%d\n", map_fd, errno);
|
||||||
|
prepare_reuseport_grp(SOCK_STREAM, map_fd, &fd64, &sk_cookie, 1);
|
||||||
|
fd = fd64;
|
||||||
|
err = bpf_map_update_elem(map_fd, &index3, &fd, BPF_NOEXIST);
|
||||||
|
CHECK(err == -1, "reuseport array update 32 bit fd",
|
||||||
|
"err:%d errno:%d\n", err, errno);
|
||||||
|
err = bpf_map_lookup_elem(map_fd, &index3, &map_cookie);
|
||||||
|
CHECK(err != -1 || errno != ENOSPC,
|
||||||
|
"reuseport array lookup 32 bit fd",
|
||||||
|
"err:%d errno:%d\n", err, errno);
|
||||||
|
close(fd);
|
||||||
|
close(map_fd);
|
||||||
|
}
|
||||||
|
|
||||||
static void run_all_tests(void)
|
static void run_all_tests(void)
|
||||||
{
|
{
|
||||||
test_hashmap(0, NULL);
|
test_hashmap(0, NULL);
|
||||||
|
@ -1170,6 +1428,8 @@ static void run_all_tests(void)
|
||||||
|
|
||||||
test_map_rdonly();
|
test_map_rdonly();
|
||||||
test_map_wronly();
|
test_map_wronly();
|
||||||
|
|
||||||
|
test_reuseport_array();
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(void)
|
int main(void)
|
||||||
|
|
Loading…
Reference in a new issue