Commit daa5fc71 authored by David S. Miller's avatar David S. Miller

Merge branch 'bpf-selftests-improve-and-use-library'

Mickaël Salaün says:

====================
Improve BPF selftests and use the library (net-next tree)

This series brings some fixes to selftests, add the ability to test
unprivileged BPF programs as root and replace bpf_sys.h with calls to the BPF
library.

This is intended for the net-next tree and apply on c0e4dadb ("net: dsa:
mv88e6xxx: Move forward declaration to where it is needed").

Changes since v4:
* align text for function calls as requested by Daniel Borkmann
  (bpf_load_program and bpf_map_update_elem)
* rebase

Changes since v3:
* keep the bzero() calls

Changes since v2:
* use the patches from two previous series (unprivileged tests and bpf_sys.h
  replacement)
* include one more stdint.h
* rebase on net-next
* add this cover letter

Changes since v1:
* exclude patches not intended for the net-next tree
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents e40d5d78 bc6a3d99
......@@ -63,6 +63,12 @@ struct bpf_insn {
__s32 imm; /* signed immediate constant */
};
/* Key of an a BPF_MAP_TYPE_LPM_TRIE entry */
struct bpf_lpm_trie_key {
__u32 prefixlen; /* up to 32 for AF_INET, 128 for AF_INET6 */
__u8 data[0]; /* Arbitrary size */
};
/* BPF syscall commands, see bpf(2) man-page for details. */
enum bpf_cmd {
BPF_MAP_CREATE,
......@@ -89,6 +95,7 @@ enum bpf_map_type {
BPF_MAP_TYPE_CGROUP_ARRAY,
BPF_MAP_TYPE_LRU_HASH,
BPF_MAP_TYPE_LRU_PERCPU_HASH,
BPF_MAP_TYPE_LPM_TRIE,
};
enum bpf_prog_type {
......@@ -430,6 +437,18 @@ union bpf_attr {
* @xdp_md: pointer to xdp_md
* @delta: An positive/negative integer to be added to xdp_md.data
* Return: 0 on success or negative on error
*
* int bpf_probe_read_str(void *dst, int size, const void *unsafe_ptr)
* Copy a NUL terminated string from unsafe address. In case the string
* length is smaller than size, the target is not padded with further NUL
* bytes. In case the string length is larger than size, just count-1
* bytes are copied and the last byte is set to NUL.
* @dst: destination address
* @size: maximum number of bytes to copy, including the trailing NUL
* @unsafe_ptr: unsafe address
* Return:
* > 0 length of the string including the trailing NUL on success
* < 0 error
*/
#define __BPF_FUNC_MAPPER(FN) \
FN(unspec), \
......@@ -476,7 +495,8 @@ union bpf_attr {
FN(set_hash_invalid), \
FN(get_numa_node_id), \
FN(skb_change_head), \
FN(xdp_adjust_head),
FN(xdp_adjust_head), \
FN(probe_read_str),
/* integer value in 'imm' field of BPF_CALL instruction selects which helper
* function eBPF program intends to call
......@@ -502,6 +522,7 @@ enum bpf_func_id {
/* BPF_FUNC_l4_csum_replace flags. */
#define BPF_F_PSEUDO_HDR (1ULL << 4)
#define BPF_F_MARK_MANGLED_0 (1ULL << 5)
#define BPF_F_MARK_ENFORCE (1ULL << 6)
/* BPF_FUNC_clone_redirect and BPF_FUNC_redirect flags. */
#define BPF_F_INGRESS (1ULL << 0)
......
......@@ -42,7 +42,7 @@
# endif
#endif
static __u64 ptr_to_u64(void *ptr)
static __u64 ptr_to_u64(const void *ptr)
{
return (__u64) (unsigned long) ptr;
}
......@@ -50,7 +50,13 @@ static __u64 ptr_to_u64(void *ptr)
static int sys_bpf(enum bpf_cmd cmd, union bpf_attr *attr,
unsigned int size)
{
#ifdef __NR_bpf
return syscall(__NR_bpf, cmd, attr, size);
#else
fprintf(stderr, "No bpf syscall, kernel headers too old?\n");
errno = ENOSYS;
return -1;
#endif
}
int bpf_create_map(enum bpf_map_type map_type, int key_size,
......@@ -69,8 +75,8 @@ int bpf_create_map(enum bpf_map_type map_type, int key_size,
return sys_bpf(BPF_MAP_CREATE, &attr, sizeof(attr));
}
int bpf_load_program(enum bpf_prog_type type, struct bpf_insn *insns,
size_t insns_cnt, char *license,
int bpf_load_program(enum bpf_prog_type type, const struct bpf_insn *insns,
size_t insns_cnt, const char *license,
__u32 kern_version, char *log_buf, size_t log_buf_sz)
{
int fd;
......@@ -98,7 +104,7 @@ int bpf_load_program(enum bpf_prog_type type, struct bpf_insn *insns,
return sys_bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
}
int bpf_map_update_elem(int fd, void *key, void *value,
int bpf_map_update_elem(int fd, const void *key, const void *value,
__u64 flags)
{
union bpf_attr attr;
......@@ -112,7 +118,7 @@ int bpf_map_update_elem(int fd, void *key, void *value,
return sys_bpf(BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr));
}
int bpf_map_lookup_elem(int fd, void *key, void *value)
int bpf_map_lookup_elem(int fd, const void *key, void *value)
{
union bpf_attr attr;
......@@ -124,7 +130,7 @@ int bpf_map_lookup_elem(int fd, void *key, void *value)
return sys_bpf(BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr));
}
int bpf_map_delete_elem(int fd, void *key)
int bpf_map_delete_elem(int fd, const void *key)
{
union bpf_attr attr;
......@@ -135,7 +141,7 @@ int bpf_map_delete_elem(int fd, void *key)
return sys_bpf(BPF_MAP_DELETE_ELEM, &attr, sizeof(attr));
}
int bpf_map_get_next_key(int fd, void *key, void *next_key)
int bpf_map_get_next_key(int fd, const void *key, void *next_key)
{
union bpf_attr attr;
......
......@@ -28,17 +28,17 @@ int bpf_create_map(enum bpf_map_type map_type, int key_size, int value_size,
/* Recommend log buffer size */
#define BPF_LOG_BUF_SIZE 65536
int bpf_load_program(enum bpf_prog_type type, struct bpf_insn *insns,
size_t insns_cnt, char *license,
int bpf_load_program(enum bpf_prog_type type, const struct bpf_insn *insns,
size_t insns_cnt, const char *license,
__u32 kern_version, char *log_buf,
size_t log_buf_sz);
int bpf_map_update_elem(int fd, void *key, void *value,
int bpf_map_update_elem(int fd, const void *key, const void *value,
__u64 flags);
int bpf_map_lookup_elem(int fd, void *key, void *value);
int bpf_map_delete_elem(int fd, void *key);
int bpf_map_get_next_key(int fd, void *key, void *next_key);
int bpf_map_lookup_elem(int fd, const void *key, void *value);
int bpf_map_delete_elem(int fd, const void *key);
int bpf_map_get_next_key(int fd, const void *key, void *next_key);
int bpf_obj_pin(int fd, const char *pathname);
int bpf_obj_get(const char *pathname);
int bpf_prog_attach(int prog_fd, int attachable_fd, enum bpf_attach_type type);
......
......@@ -2,3 +2,4 @@ test_verifier
test_maps
test_lru_map
test_lpm_map
test_tag
CFLAGS += -Wall -O2 -I../../../../usr/include
CFLAGS += -Wall -O2 -lcap -I../../../include/uapi -I../../../lib
test_objs = test_verifier test_tag test_maps test_lru_map test_lpm_map
......@@ -7,6 +7,8 @@ TEST_FILES := $(test_objs)
all: $(test_objs)
$(test_objs): ../../../lib/bpf/bpf.o
include ../lib.mk
clean:
......
#ifndef __BPF_SYS__
#define __BPF_SYS__
#include <stdint.h>
#include <stdlib.h>
#include <sys/syscall.h>
#include <linux/bpf.h>
static inline __u64 bpf_ptr_to_u64(const void *ptr)
{
return (__u64)(unsigned long) ptr;
}
static inline int bpf(int cmd, union bpf_attr *attr, unsigned int size)
{
#ifdef __NR_bpf
return syscall(__NR_bpf, cmd, attr, size);
#else
fprintf(stderr, "No bpf syscall, kernel headers too old?\n");
errno = ENOSYS;
return -1;
#endif
}
static inline int bpf_map_lookup(int fd, const void *key, void *value)
{
union bpf_attr attr = {};
attr.map_fd = fd;
attr.key = bpf_ptr_to_u64(key);
attr.value = bpf_ptr_to_u64(value);
return bpf(BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr));
}
static inline int bpf_map_update(int fd, const void *key, const void *value,
uint64_t flags)
{
union bpf_attr attr = {};
attr.map_fd = fd;
attr.key = bpf_ptr_to_u64(key);
attr.value = bpf_ptr_to_u64(value);
attr.flags = flags;
return bpf(BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr));
}
static inline int bpf_map_delete(int fd, const void *key)
{
union bpf_attr attr = {};
attr.map_fd = fd;
attr.key = bpf_ptr_to_u64(key);
return bpf(BPF_MAP_DELETE_ELEM, &attr, sizeof(attr));
}
static inline int bpf_map_next_key(int fd, const void *key, void *next_key)
{
union bpf_attr attr = {};
attr.map_fd = fd;
attr.key = bpf_ptr_to_u64(key);
attr.next_key = bpf_ptr_to_u64(next_key);
return bpf(BPF_MAP_GET_NEXT_KEY, &attr, sizeof(attr));
}
static inline int bpf_map_create(enum bpf_map_type type, uint32_t size_key,
uint32_t size_value, uint32_t max_elem,
uint32_t flags)
{
union bpf_attr attr = {};
attr.map_type = type;
attr.key_size = size_key;
attr.value_size = size_value;
attr.max_entries = max_elem;
attr.map_flags = flags;
return bpf(BPF_MAP_CREATE, &attr, sizeof(attr));
}
static inline int bpf_prog_load(enum bpf_prog_type type,
const struct bpf_insn *insns, size_t size_insns,
const char *license, char *log, size_t size_log)
{
union bpf_attr attr = {};
attr.prog_type = type;
attr.insns = bpf_ptr_to_u64(insns);
attr.insn_cnt = size_insns / sizeof(struct bpf_insn);
attr.license = bpf_ptr_to_u64(license);
if (size_log > 0) {
attr.log_buf = bpf_ptr_to_u64(log);
attr.log_size = size_log;
attr.log_level = 1;
log[0] = 0;
}
return bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
}
#endif /* __BPF_SYS__ */
......@@ -22,7 +22,7 @@
#include <sys/time.h>
#include <sys/resource.h>
#include "bpf_sys.h"
#include <bpf/bpf.h>
#include "bpf_util.h"
struct tlpm_node {
......@@ -182,7 +182,7 @@ static void test_lpm_map(int keysize)
key = alloca(sizeof(*key) + keysize);
memset(key, 0, sizeof(*key) + keysize);
map = bpf_map_create(BPF_MAP_TYPE_LPM_TRIE,
map = bpf_create_map(BPF_MAP_TYPE_LPM_TRIE,
sizeof(*key) + keysize,
keysize + 1,
4096,
......@@ -198,7 +198,7 @@ static void test_lpm_map(int keysize)
key->prefixlen = value[keysize];
memcpy(key->data, value, keysize);
r = bpf_map_update(map, key, value, 0);
r = bpf_map_update_elem(map, key, value, 0);
assert(!r);
}
......@@ -210,7 +210,7 @@ static void test_lpm_map(int keysize)
key->prefixlen = 8 * keysize;
memcpy(key->data, data, keysize);
r = bpf_map_lookup(map, key, value);
r = bpf_map_lookup_elem(map, key, value);
assert(!r || errno == ENOENT);
assert(!t == !!r);
......@@ -252,12 +252,12 @@ static void test_lpm_ipaddr(void)
key_ipv4 = alloca(key_size_ipv4);
key_ipv6 = alloca(key_size_ipv6);
map_fd_ipv4 = bpf_map_create(BPF_MAP_TYPE_LPM_TRIE,
map_fd_ipv4 = bpf_create_map(BPF_MAP_TYPE_LPM_TRIE,
key_size_ipv4, sizeof(value),
100, BPF_F_NO_PREALLOC);
assert(map_fd_ipv4 >= 0);
map_fd_ipv6 = bpf_map_create(BPF_MAP_TYPE_LPM_TRIE,
map_fd_ipv6 = bpf_create_map(BPF_MAP_TYPE_LPM_TRIE,
key_size_ipv6, sizeof(value),
100, BPF_F_NO_PREALLOC);
assert(map_fd_ipv6 >= 0);
......@@ -266,32 +266,32 @@ static void test_lpm_ipaddr(void)
value = 1;
key_ipv4->prefixlen = 16;
inet_pton(AF_INET, "192.168.0.0", key_ipv4->data);
assert(bpf_map_update(map_fd_ipv4, key_ipv4, &value, 0) == 0);
assert(bpf_map_update_elem(map_fd_ipv4, key_ipv4, &value, 0) == 0);
value = 2;
key_ipv4->prefixlen = 24;
inet_pton(AF_INET, "192.168.0.0", key_ipv4->data);
assert(bpf_map_update(map_fd_ipv4, key_ipv4, &value, 0) == 0);
assert(bpf_map_update_elem(map_fd_ipv4, key_ipv4, &value, 0) == 0);
value = 3;
key_ipv4->prefixlen = 24;
inet_pton(AF_INET, "192.168.128.0", key_ipv4->data);
assert(bpf_map_update(map_fd_ipv4, key_ipv4, &value, 0) == 0);
assert(bpf_map_update_elem(map_fd_ipv4, key_ipv4, &value, 0) == 0);
value = 5;
key_ipv4->prefixlen = 24;
inet_pton(AF_INET, "192.168.1.0", key_ipv4->data);
assert(bpf_map_update(map_fd_ipv4, key_ipv4, &value, 0) == 0);
assert(bpf_map_update_elem(map_fd_ipv4, key_ipv4, &value, 0) == 0);
value = 4;
key_ipv4->prefixlen = 23;
inet_pton(AF_INET, "192.168.0.0", key_ipv4->data);
assert(bpf_map_update(map_fd_ipv4, key_ipv4, &value, 0) == 0);
assert(bpf_map_update_elem(map_fd_ipv4, key_ipv4, &value, 0) == 0);
value = 0xdeadbeef;
key_ipv6->prefixlen = 64;
inet_pton(AF_INET6, "2a00:1450:4001:814::200e", key_ipv6->data);
assert(bpf_map_update(map_fd_ipv6, key_ipv6, &value, 0) == 0);
assert(bpf_map_update_elem(map_fd_ipv6, key_ipv6, &value, 0) == 0);
/* Set tprefixlen to maximum for lookups */
key_ipv4->prefixlen = 32;
......@@ -299,32 +299,32 @@ static void test_lpm_ipaddr(void)
/* Test some lookups that should come back with a value */
inet_pton(AF_INET, "192.168.128.23", key_ipv4->data);
assert(bpf_map_lookup(map_fd_ipv4, key_ipv4, &value) == 0);
assert(bpf_map_lookup_elem(map_fd_ipv4, key_ipv4, &value) == 0);
assert(value == 3);
inet_pton(AF_INET, "192.168.0.1", key_ipv4->data);
assert(bpf_map_lookup(map_fd_ipv4, key_ipv4, &value) == 0);
assert(bpf_map_lookup_elem(map_fd_ipv4, key_ipv4, &value) == 0);
assert(value == 2);
inet_pton(AF_INET6, "2a00:1450:4001:814::", key_ipv6->data);
assert(bpf_map_lookup(map_fd_ipv6, key_ipv6, &value) == 0);
assert(bpf_map_lookup_elem(map_fd_ipv6, key_ipv6, &value) == 0);
assert(value == 0xdeadbeef);
inet_pton(AF_INET6, "2a00:1450:4001:814::1", key_ipv6->data);
assert(bpf_map_lookup(map_fd_ipv6, key_ipv6, &value) == 0);
assert(bpf_map_lookup_elem(map_fd_ipv6, key_ipv6, &value) == 0);
assert(value == 0xdeadbeef);
/* Test some lookups that should not match any entry */
inet_pton(AF_INET, "10.0.0.1", key_ipv4->data);
assert(bpf_map_lookup(map_fd_ipv4, key_ipv4, &value) == -1 &&
assert(bpf_map_lookup_elem(map_fd_ipv4, key_ipv4, &value) == -1 &&
errno == ENOENT);
inet_pton(AF_INET, "11.11.11.11", key_ipv4->data);
assert(bpf_map_lookup(map_fd_ipv4, key_ipv4, &value) == -1 &&
assert(bpf_map_lookup_elem(map_fd_ipv4, key_ipv4, &value) == -1 &&
errno == ENOENT);
inet_pton(AF_INET6, "2a00:ffff::", key_ipv6->data);
assert(bpf_map_lookup(map_fd_ipv6, key_ipv6, &value) == -1 &&
assert(bpf_map_lookup_elem(map_fd_ipv6, key_ipv6, &value) == -1 &&
errno == ENOENT);
close(map_fd_ipv4);
......
This diff is collapsed.
This diff is collapsed.
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
......@@ -16,9 +17,9 @@
#include <linux/bpf.h>
#include <linux/if_alg.h>
#include "../../../include/linux/filter.h"
#include <bpf/bpf.h>
#include "bpf_sys.h"
#include "../../../include/linux/filter.h"
static struct bpf_insn prog[BPF_MAXINSNS];
......@@ -55,8 +56,8 @@ static int bpf_try_load_prog(int insns, int fd_map,
int fd_prog;
bpf_filler(insns, fd_map);
fd_prog = bpf_prog_load(BPF_PROG_TYPE_SCHED_CLS, prog, insns *
sizeof(struct bpf_insn), "", NULL, 0);
fd_prog = bpf_load_program(BPF_PROG_TYPE_SCHED_CLS, prog, insns, "", 0,
NULL, 0);
assert(fd_prog > 0);
if (fd_map > 0)
bpf_filler(insns, 0);
......@@ -187,7 +188,7 @@ int main(void)
int i, fd_map;
setrlimit(RLIMIT_MEMLOCK, &rinf);
fd_map = bpf_map_create(BPF_MAP_TYPE_HASH, sizeof(int),
fd_map = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(int),
sizeof(int), 1, BPF_F_NO_PREALLOC);
assert(fd_map > 0);
......
......@@ -8,7 +8,9 @@
* License as published by the Free Software Foundation.
*/
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
......@@ -16,6 +18,7 @@
#include <stdbool.h>
#include <sched.h>
#include <sys/capability.h>
#include <sys/resource.h>
#include <linux/unistd.h>
......@@ -23,9 +26,9 @@
#include <linux/bpf_perf_event.h>
#include <linux/bpf.h>
#include "../../../include/linux/filter.h"
#include <bpf/bpf.h>
#include "bpf_sys.h"
#include "../../../include/linux/filter.h"
#ifndef ARRAY_SIZE
# define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
......@@ -4464,7 +4467,7 @@ static int create_map(uint32_t size_value, uint32_t max_elem)
{
int fd;
fd = bpf_map_create(BPF_MAP_TYPE_HASH, sizeof(long long),
fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(long long),
size_value, max_elem, BPF_F_NO_PREALLOC);
if (fd < 0)
printf("Failed to create hash map '%s'!\n", strerror(errno));
......@@ -4476,7 +4479,7 @@ static int create_prog_array(void)
{
int fd;
fd = bpf_map_create(BPF_MAP_TYPE_PROG_ARRAY, sizeof(int),
fd = bpf_create_map(BPF_MAP_TYPE_PROG_ARRAY, sizeof(int),
sizeof(int), 4, 0);
if (fd < 0)
printf("Failed to create prog array '%s'!\n", strerror(errno));
......@@ -4534,9 +4537,9 @@ static void do_test_single(struct bpf_test *test, bool unpriv,
do_test_fixup(test, prog, &fd_f1, &fd_f2, &fd_f3);
fd_prog = bpf_prog_load(prog_type ? : BPF_PROG_TYPE_SOCKET_FILTER,
prog, prog_len * sizeof(struct bpf_insn),
"GPL", bpf_vlog, sizeof(bpf_vlog));
fd_prog = bpf_load_program(prog_type ? : BPF_PROG_TYPE_SOCKET_FILTER,
prog, prog_len, "GPL", 0, bpf_vlog,
sizeof(bpf_vlog));
expected_ret = unpriv && test->result_unpriv != UNDEF ?
test->result_unpriv : test->result;
......@@ -4574,6 +4577,55 @@ static void do_test_single(struct bpf_test *test, bool unpriv,
goto close_fds;
}
static bool is_admin(void)
{
cap_t caps;
cap_flag_value_t sysadmin = CAP_CLEAR;
const cap_value_t cap_val = CAP_SYS_ADMIN;
if (!CAP_IS_SUPPORTED(CAP_SETFCAP)) {
perror("cap_get_flag");
return false;
}
caps = cap_get_proc();
if (!caps) {
perror("cap_get_proc");
return false;
}
if (cap_get_flag(caps, cap_val, CAP_EFFECTIVE, &sysadmin))
perror("cap_get_flag");
if (cap_free(caps))
perror("cap_free");
return (sysadmin == CAP_SET);
}
static int set_admin(bool admin)
{
cap_t caps;
const cap_value_t cap_val = CAP_SYS_ADMIN;
int ret = -1;
caps = cap_get_proc();
if (!caps) {
perror("cap_get_proc");
return -1;
}
if (cap_set_flag(caps, CAP_EFFECTIVE, 1, &cap_val,
admin ? CAP_SET : CAP_CLEAR)) {
perror("cap_set_flag");
goto out;
}
if (cap_set_proc(caps)) {
perror("cap_set_proc");
goto out;
}
ret = 0;
out:
if (cap_free(caps))
perror("cap_free");
return ret;
}
static int do_test(bool unpriv, unsigned int from, unsigned int to)
{
int i, passes = 0, errors = 0;
......@@ -4584,11 +4636,19 @@ static int do_test(bool unpriv, unsigned int from, unsigned int to)
/* Program types that are not supported by non-root we
* skip right away.
*/
if (unpriv && test->prog_type)
continue;
if (!test->prog_type) {
if (!unpriv)
set_admin(false);
printf("#%d/u %s ", i, test->descr);
do_test_single(test, true, &passes, &errors);
if (!unpriv)
set_admin(true);
}
printf("#%d %s ", i, test->descr);
do_test_single(test, unpriv, &passes, &errors);
if (!unpriv) {
printf("#%d/p %s ", i, test->descr);
do_test_single(test, false, &passes, &errors);
}
}
printf("Summary: %d PASSED, %d FAILED\n", passes, errors);
......@@ -4600,7 +4660,7 @@ int main(int argc, char **argv)
struct rlimit rinf = { RLIM_INFINITY, RLIM_INFINITY };
struct rlimit rlim = { 1 << 20, 1 << 20 };
unsigned int from = 0, to = ARRAY_SIZE(tests);
bool unpriv = geteuid() != 0;
bool unpriv = !is_admin();
if (argc == 3) {
unsigned int l = atoi(argv[argc - 2]);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment