Merge branch 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git

This commit is contained in:
Stephen Rothwell 2024-12-20 11:48:40 +11:00
commit 49e3ee1413
114 changed files with 3474 additions and 1718 deletions

View File

@ -2299,6 +2299,14 @@ void __bpf_obj_drop_impl(void *p, const struct btf_record *rec, bool percpu);
struct bpf_map *bpf_map_get(u32 ufd);
struct bpf_map *bpf_map_get_with_uref(u32 ufd);
/*
* The __bpf_map_get() and __btf_get_by_fd() functions parse a file
* descriptor and return a corresponding map or btf object.
* Their names are double underscored to emphasize the fact that they
* do not increase refcnt. To also increase refcnt use corresponding
* bpf_map_get() and btf_get_by_fd() functions.
*/
static inline struct bpf_map *__bpf_map_get(struct fd f)
{
if (fd_empty(f))
@ -2308,6 +2316,15 @@ static inline struct bpf_map *__bpf_map_get(struct fd f)
return fd_file(f)->private_data;
}
static inline struct btf *__btf_get_by_fd(struct fd f)
{
if (fd_empty(f))
return ERR_PTR(-EBADF);
if (unlikely(fd_file(f)->f_op != &btf_fops))
return ERR_PTR(-EINVAL);
return fd_file(f)->private_data;
}
void bpf_map_inc(struct bpf_map *map);
void bpf_map_inc_with_uref(struct bpf_map *map);
struct bpf_map *__bpf_map_inc_not_zero(struct bpf_map *map, bool uref);

View File

@ -233,6 +233,7 @@ enum bpf_stack_slot_type {
*/
STACK_DYNPTR,
STACK_ITER,
STACK_IRQ_FLAG,
};
#define BPF_REG_SIZE 8 /* size of eBPF register in bytes */
@ -254,8 +255,9 @@ struct bpf_reference_state {
* default to pointer reference on zero initialization of a state.
*/
enum ref_state_type {
REF_TYPE_PTR = 0,
REF_TYPE_LOCK,
REF_TYPE_PTR = 1,
REF_TYPE_IRQ = 2,
REF_TYPE_LOCK = 3,
} type;
/* Track each reference created with a unique id, even if the same
* instruction creates the reference multiple times (eg, via CALL).
@ -315,9 +317,6 @@ struct bpf_func_state {
u32 callback_depth;
/* The following fields should be last. See copy_func_state() */
int acquired_refs;
int active_locks;
struct bpf_reference_state *refs;
/* The state of the stack. Each element of the array describes BPF_REG_SIZE
* (i.e. 8) bytes worth of stack memory.
* stack[0] represents bytes [*(r10-8)..*(r10-1)]
@ -370,6 +369,8 @@ struct bpf_verifier_state {
/* call stack tracking */
struct bpf_func_state *frame[MAX_CALL_FRAMES];
struct bpf_verifier_state *parent;
/* Acquired reference states */
struct bpf_reference_state *refs;
/*
* 'branches' field is the number of branches left to explore:
* 0 - all possible paths from this state reached bpf_exit or
@ -419,9 +420,13 @@ struct bpf_verifier_state {
u32 insn_idx;
u32 curframe;
bool speculative;
u32 acquired_refs;
u32 active_locks;
u32 active_preempt_locks;
u32 active_irq_id;
bool active_rcu_lock;
u32 active_preempt_lock;
bool speculative;
/* If this state was ever pointed-to by other state's loop_entry field
* this flag would be set to true. Used to avoid freeing such states
* while they are still in use.
@ -980,8 +985,9 @@ const char *dynptr_type_str(enum bpf_dynptr_type type);
const char *iter_type_str(const struct btf *btf, u32 btf_id);
const char *iter_state_str(enum bpf_iter_state state);
void print_verifier_state(struct bpf_verifier_env *env,
const struct bpf_func_state *state, bool print_all);
void print_insn_state(struct bpf_verifier_env *env, const struct bpf_func_state *state);
void print_verifier_state(struct bpf_verifier_env *env, const struct bpf_verifier_state *vstate,
u32 frameno, bool print_all);
void print_insn_state(struct bpf_verifier_env *env, const struct bpf_verifier_state *vstate,
u32 frameno);
#endif /* _LINUX_BPF_VERIFIER_H */

View File

@ -1573,6 +1573,16 @@ union bpf_attr {
* If provided, prog_flags should have BPF_F_TOKEN_FD flag set.
*/
__s32 prog_token_fd;
/* The fd_array_cnt can be used to pass the length of the
* fd_array array. In this case all the [map] file descriptors
* passed in this array will be bound to the program, even if
* the maps are not referenced directly. The functionality is
* similar to the BPF_PROG_BIND_MAP syscall, but maps can be
* used by the verifier during the program load. If provided,
* then the fd_array[0,...,fd_array_cnt-1] is expected to be
* continuous.
*/
__u32 fd_array_cnt;
};
struct { /* anonymous struct used by BPF_OBJ_* commands */

View File

@ -7887,14 +7887,9 @@ struct btf *btf_get_by_fd(int fd)
struct btf *btf;
CLASS(fd, f)(fd);
if (fd_empty(f))
return ERR_PTR(-EBADF);
if (fd_file(f)->f_op != &btf_fops)
return ERR_PTR(-EINVAL);
btf = fd_file(f)->private_data;
refcount_inc(&btf->refcnt);
btf = __btf_get_by_fd(f);
if (!IS_ERR(btf))
refcount_inc(&btf->refcnt);
return btf;
}

View File

@ -3057,6 +3057,21 @@ __bpf_kfunc int bpf_copy_from_user_str(void *dst, u32 dst__sz, const void __user
return ret + 1;
}
/* Keep unsinged long in prototype so that kfunc is usable when emitted to
* vmlinux.h in BPF programs directly, but note that while in BPF prog, the
* unsigned long always points to 8-byte region on stack, the kernel may only
* read and write the 4-bytes on 32-bit.
*/
__bpf_kfunc void bpf_local_irq_save(unsigned long *flags__irq_flag)
{
local_irq_save(*flags__irq_flag);
}
__bpf_kfunc void bpf_local_irq_restore(unsigned long *flags__irq_flag)
{
local_irq_restore(*flags__irq_flag);
}
__bpf_kfunc_end_defs();
BTF_KFUNCS_START(generic_btf_ids)
@ -3089,7 +3104,9 @@ BTF_ID_FLAGS(func, bpf_task_get_cgroup1, KF_ACQUIRE | KF_RCU | KF_RET_NULL)
BTF_ID_FLAGS(func, bpf_task_from_pid, KF_ACQUIRE | KF_RET_NULL)
BTF_ID_FLAGS(func, bpf_task_from_vpid, KF_ACQUIRE | KF_RET_NULL)
BTF_ID_FLAGS(func, bpf_throw)
#ifdef CONFIG_BPF_EVENTS
BTF_ID_FLAGS(func, bpf_send_signal_task, KF_TRUSTED_ARGS)
#endif
BTF_KFUNCS_END(generic_btf_ids)
static const struct btf_kfunc_id_set generic_kfunc_set = {
@ -3135,7 +3152,9 @@ BTF_ID_FLAGS(func, bpf_dynptr_is_null)
BTF_ID_FLAGS(func, bpf_dynptr_is_rdonly)
BTF_ID_FLAGS(func, bpf_dynptr_size)
BTF_ID_FLAGS(func, bpf_dynptr_clone)
#ifdef CONFIG_NET
BTF_ID_FLAGS(func, bpf_modify_return_test_tp)
#endif
BTF_ID_FLAGS(func, bpf_wq_init)
BTF_ID_FLAGS(func, bpf_wq_set_callback_impl)
BTF_ID_FLAGS(func, bpf_wq_start)
@ -3149,6 +3168,8 @@ BTF_ID_FLAGS(func, bpf_get_kmem_cache)
BTF_ID_FLAGS(func, bpf_iter_kmem_cache_new, KF_ITER_NEW | KF_SLEEPABLE)
BTF_ID_FLAGS(func, bpf_iter_kmem_cache_next, KF_ITER_NEXT | KF_RET_NULL | KF_SLEEPABLE)
BTF_ID_FLAGS(func, bpf_iter_kmem_cache_destroy, KF_ITER_DESTROY | KF_SLEEPABLE)
BTF_ID_FLAGS(func, bpf_local_irq_save)
BTF_ID_FLAGS(func, bpf_local_irq_restore)
BTF_KFUNCS_END(common_btf_ids)
static const struct btf_kfunc_id_set common_kfunc_set = {

View File

@ -537,6 +537,7 @@ static char slot_type_char[] = {
[STACK_ZERO] = '0',
[STACK_DYNPTR] = 'd',
[STACK_ITER] = 'i',
[STACK_IRQ_FLAG] = 'f'
};
static void print_liveness(struct bpf_verifier_env *env,
@ -753,9 +754,10 @@ static void print_reg_state(struct bpf_verifier_env *env,
verbose(env, ")");
}
void print_verifier_state(struct bpf_verifier_env *env, const struct bpf_func_state *state,
bool print_all)
void print_verifier_state(struct bpf_verifier_env *env, const struct bpf_verifier_state *vstate,
u32 frameno, bool print_all)
{
const struct bpf_func_state *state = vstate->frame[frameno];
const struct bpf_reg_state *reg;
int i;
@ -843,11 +845,11 @@ void print_verifier_state(struct bpf_verifier_env *env, const struct bpf_func_st
break;
}
}
if (state->acquired_refs && state->refs[0].id) {
verbose(env, " refs=%d", state->refs[0].id);
for (i = 1; i < state->acquired_refs; i++)
if (state->refs[i].id)
verbose(env, ",%d", state->refs[i].id);
if (vstate->acquired_refs && vstate->refs[0].id) {
verbose(env, " refs=%d", vstate->refs[0].id);
for (i = 1; i < vstate->acquired_refs; i++)
if (vstate->refs[i].id)
verbose(env, ",%d", vstate->refs[i].id);
}
if (state->in_callback_fn)
verbose(env, " cb");
@ -864,7 +866,8 @@ static inline u32 vlog_alignment(u32 pos)
BPF_LOG_MIN_ALIGNMENT) - pos - 1;
}
void print_insn_state(struct bpf_verifier_env *env, const struct bpf_func_state *state)
void print_insn_state(struct bpf_verifier_env *env, const struct bpf_verifier_state *vstate,
u32 frameno)
{
if (env->prev_log_pos && env->prev_log_pos == env->log.end_pos) {
/* remove new line character */
@ -873,5 +876,5 @@ void print_insn_state(struct bpf_verifier_env *env, const struct bpf_func_state
} else {
verbose(env, "%d:", env->insn_idx);
}
print_verifier_state(env, state, false);
print_verifier_state(env, vstate, frameno, false);
}

View File

@ -2730,7 +2730,7 @@ static bool is_perfmon_prog_type(enum bpf_prog_type prog_type)
}
/* last field in 'union bpf_attr' used by this command */
#define BPF_PROG_LOAD_LAST_FIELD prog_token_fd
#define BPF_PROG_LOAD_LAST_FIELD fd_array_cnt
static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size)
{

File diff suppressed because it is too large Load Diff

View File

@ -357,17 +357,6 @@ static const struct bpf_func_proto bpf_probe_write_user_proto = {
.arg3_type = ARG_CONST_SIZE,
};
static const struct bpf_func_proto *bpf_get_probe_write_proto(void)
{
if (!capable(CAP_SYS_ADMIN))
return NULL;
pr_warn_ratelimited("%s[%d] is installing a program with bpf_probe_write_user helper that may corrupt user memory!",
current->comm, task_pid_nr(current));
return &bpf_probe_write_user_proto;
}
#define MAX_TRACE_PRINTK_VARARGS 3
#define BPF_TRACE_PRINTK_SIZE 1024
@ -1444,6 +1433,8 @@ late_initcall(bpf_key_sig_kfuncs_init);
static const struct bpf_func_proto *
bpf_tracing_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
{
const struct bpf_func_proto *func_proto;
switch (func_id) {
case BPF_FUNC_map_lookup_elem:
return &bpf_map_lookup_elem_proto;
@ -1485,9 +1476,6 @@ bpf_tracing_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
return &bpf_perf_event_read_proto;
case BPF_FUNC_get_prandom_u32:
return &bpf_get_prandom_u32_proto;
case BPF_FUNC_probe_write_user:
return security_locked_down(LOCKDOWN_BPF_WRITE_USER) < 0 ?
NULL : bpf_get_probe_write_proto();
case BPF_FUNC_probe_read_user:
return &bpf_probe_read_user_proto;
case BPF_FUNC_probe_read_kernel:
@ -1566,7 +1554,22 @@ bpf_tracing_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
case BPF_FUNC_trace_vprintk:
return bpf_get_trace_vprintk_proto();
default:
return bpf_base_func_proto(func_id, prog);
break;
}
func_proto = bpf_base_func_proto(func_id, prog);
if (func_proto)
return func_proto;
if (!bpf_token_capable(prog->aux->token, CAP_SYS_ADMIN))
return NULL;
switch (func_id) {
case BPF_FUNC_probe_write_user:
return security_locked_down(LOCKDOWN_BPF_WRITE_USER) < 0 ?
NULL : &bpf_probe_write_user_proto;
default:
return NULL;
}
}

View File

@ -1018,6 +1018,7 @@ int bpf_prog_test_run_skb(struct bpf_prog *prog, const union bpf_attr *kattr,
case BPF_PROG_TYPE_LWT_IN:
case BPF_PROG_TYPE_LWT_OUT:
case BPF_PROG_TYPE_LWT_XMIT:
case BPF_PROG_TYPE_CGROUP_SKB:
is_direct_pkt_access = true;
break;
default:

View File

@ -123,7 +123,7 @@ always-y += ibumad_kern.o
always-y += hbm_out_kern.o
always-y += hbm_edt_kern.o
TPROGS_CFLAGS = $(TPROGS_USER_CFLAGS)
COMMON_CFLAGS = $(TPROGS_USER_CFLAGS)
TPROGS_LDFLAGS = $(TPROGS_USER_LDFLAGS)
ifeq ($(ARCH), arm)

View File

@ -63,7 +63,6 @@ SEC("tc_mark")
int _tc_mark(struct __sk_buff *ctx)
{
void *data = (void *)(unsigned long)ctx->data;
void *data_end = (void *)(unsigned long)ctx->data_end;
void *data_meta = (void *)(unsigned long)ctx->data_meta;
struct meta_info *meta = data_meta;

View File

@ -283,7 +283,11 @@ vmlinux_link vmlinux
# fill in BTF IDs
if is_enabled CONFIG_DEBUG_INFO_BTF; then
info BTFIDS vmlinux
${RESOLVE_BTFIDS} vmlinux
RESOLVE_BTFIDS_ARGS=""
if is_enabled CONFIG_WERROR; then
RESOLVE_BTFIDS_ARGS=" --fatal_warnings "
fi
${RESOLVE_BTFIDS} ${RESOLVE_BTFIDS_ARGS} vmlinux
fi
mksysmap vmlinux System.map

View File

@ -13,7 +13,6 @@ static struct security_hook_list bpf_lsm_hooks[] __ro_after_init = {
#include <linux/lsm_hook_defs.h>
#undef LSM_HOOK
LSM_HOOK_INIT(inode_free_security, bpf_inode_storage_free),
LSM_HOOK_INIT(task_free, bpf_task_storage_free),
};
static const struct lsm_id bpf_lsmid = {

View File

@ -24,7 +24,7 @@ BTF COMMANDS
=============
| **bpftool** **btf** { **show** | **list** } [**id** *BTF_ID*]
| **bpftool** **btf dump** *BTF_SRC* [**format** *FORMAT*]
| **bpftool** **btf dump** *BTF_SRC* [**format** *FORMAT*] [**root_id** *ROOT_ID*]
| **bpftool** **btf help**
|
| *BTF_SRC* := { **id** *BTF_ID* | **prog** *PROG* | **map** *MAP* [{**key** | **value** | **kv** | **all**}] | **file** *FILE* }
@ -43,7 +43,7 @@ bpftool btf { show | list } [id *BTF_ID*]
that hold open file descriptors (FDs) against BTF objects. On such kernels
bpftool will automatically emit this information as well.
bpftool btf dump *BTF_SRC*
bpftool btf dump *BTF_SRC* [format *FORMAT*] [root_id *ROOT_ID*]
Dump BTF entries from a given *BTF_SRC*.
When **id** is specified, BTF object with that ID will be loaded and all
@ -67,6 +67,11 @@ bpftool btf dump *BTF_SRC*
formatting, the output is sorted by default. Use the **unsorted** option
to avoid sorting the output.
**root_id** option can be used to filter a dump to a single type and all
its dependent types. It cannot be used with any other types of filtering
(such as the "key", "value", or "kv" arguments when dumping BTF for a map).
It can be passed multiple times to dump multiple types.
bpftool btf help
Print short help message.

View File

@ -930,19 +930,24 @@ _bpftool()
format)
COMPREPLY=( $( compgen -W "c raw" -- "$cur" ) )
;;
root_id)
return 0;
;;
c)
COMPREPLY=( $( compgen -W "unsorted" -- "$cur" ) )
COMPREPLY=( $( compgen -W "unsorted root_id" -- "$cur" ) )
;;
*)
# emit extra options
case ${words[3]} in
id|file)
COMPREPLY=( $( compgen -W "root_id" -- "$cur" ) )
_bpftool_once_attr 'format'
;;
map|prog)
if [[ ${words[3]} == "map" ]] && [[ $cword == 6 ]]; then
COMPREPLY+=( $( compgen -W "key value kv all" -- "$cur" ) )
fi
COMPREPLY=( $( compgen -W "root_id" -- "$cur" ) )
_bpftool_once_attr 'format'
;;
*)

View File

@ -27,6 +27,8 @@
#define KFUNC_DECL_TAG "bpf_kfunc"
#define FASTCALL_DECL_TAG "bpf_fastcall"
#define MAX_ROOT_IDS 16
static const char * const btf_kind_str[NR_BTF_KINDS] = {
[BTF_KIND_UNKN] = "UNKNOWN",
[BTF_KIND_INT] = "INT",
@ -880,12 +882,14 @@ static int do_dump(int argc, char **argv)
{
bool dump_c = false, sort_dump_c = true;
struct btf *btf = NULL, *base = NULL;
__u32 root_type_ids[2];
__u32 root_type_ids[MAX_ROOT_IDS];
bool have_id_filtering;
int root_type_cnt = 0;
__u32 btf_id = -1;
const char *src;
int fd = -1;
int err = 0;
int i;
if (!REQ_ARGS(2)) {
usage();
@ -973,6 +977,8 @@ static int do_dump(int argc, char **argv)
goto done;
}
have_id_filtering = !!root_type_cnt;
while (argc) {
if (is_prefix(*argv, "format")) {
NEXT_ARG();
@ -992,6 +998,36 @@ static int do_dump(int argc, char **argv)
goto done;
}
NEXT_ARG();
} else if (is_prefix(*argv, "root_id")) {
__u32 root_id;
char *end;
if (have_id_filtering) {
p_err("cannot use root_id with other type filtering");
err = -EINVAL;
goto done;
} else if (root_type_cnt == MAX_ROOT_IDS) {
p_err("only %d root_id are supported", MAX_ROOT_IDS);
err = -E2BIG;
goto done;
}
NEXT_ARG();
root_id = strtoul(*argv, &end, 0);
if (*end) {
err = -1;
p_err("can't parse %s as root ID", *argv);
goto done;
}
for (i = 0; i < root_type_cnt; i++) {
if (root_type_ids[i] == root_id) {
err = -EINVAL;
p_err("duplicate root_id %d supplied", root_id);
goto done;
}
}
root_type_ids[root_type_cnt++] = root_id;
NEXT_ARG();
} else if (is_prefix(*argv, "unsorted")) {
sort_dump_c = false;
NEXT_ARG();
@ -1017,6 +1053,17 @@ static int do_dump(int argc, char **argv)
}
}
/* Invalid root IDs causes half emitted boilerplate and then unclean
* exit. It's an ugly user experience, so handle common error here.
*/
for (i = 0; i < root_type_cnt; i++) {
if (root_type_ids[i] >= btf__type_cnt(btf)) {
err = -EINVAL;
p_err("invalid root ID: %u", root_type_ids[i]);
goto done;
}
}
if (dump_c) {
if (json_output) {
p_err("JSON output for C-syntax dump is not supported");
@ -1391,7 +1438,7 @@ static int do_help(int argc, char **argv)
fprintf(stderr,
"Usage: %1$s %2$s { show | list } [id BTF_ID]\n"
" %1$s %2$s dump BTF_SRC [format FORMAT]\n"
" %1$s %2$s dump BTF_SRC [format FORMAT] [root_id ROOT_ID]\n"
" %1$s %2$s help\n"
"\n"
" BTF_SRC := { id BTF_ID | prog PROG | map MAP [{key | value | kv | all}] | file FILE }\n"

View File

@ -885,6 +885,28 @@ probe_v3_isa_extension(const char *define_prefix, __u32 ifindex)
"V3_ISA_EXTENSION");
}
/*
* Probe for the v4 instruction set extension introduced in commit 1f9a1ea821ff
* ("bpf: Support new sign-extension load insns").
*/
static void
probe_v4_isa_extension(const char *define_prefix, __u32 ifindex)
{
struct bpf_insn insns[5] = {
BPF_MOV64_IMM(BPF_REG_0, 0),
BPF_JMP32_IMM(BPF_JEQ, BPF_REG_0, 1, 1),
BPF_JMP32_A(1),
BPF_MOV64_IMM(BPF_REG_0, 1),
BPF_EXIT_INSN()
};
probe_misc_feature(insns, ARRAY_SIZE(insns),
define_prefix, ifindex,
"have_v4_isa_extension",
"ISA extension v4",
"V4_ISA_EXTENSION");
}
static void
section_system_config(enum probe_component target, const char *define_prefix)
{
@ -1029,6 +1051,7 @@ static void section_misc(const char *define_prefix, __u32 ifindex)
probe_bounded_loops(define_prefix, ifindex);
probe_v2_isa_extension(define_prefix, ifindex);
probe_v3_isa_extension(define_prefix, ifindex);
probe_v4_isa_extension(define_prefix, ifindex);
print_end_section();
}

View File

@ -141,6 +141,7 @@ struct object {
};
static int verbose;
static int warnings;
static int eprintf(int level, int var, const char *fmt, ...)
{
@ -604,6 +605,7 @@ static int symbols_resolve(struct object *obj)
if (id->id) {
pr_info("WARN: multiple IDs found for '%s': %d, %d - using %d\n",
str, id->id, type_id, id->id);
warnings++;
} else {
id->id = type_id;
(*nr)--;
@ -625,8 +627,10 @@ static int id_patch(struct object *obj, struct btf_id *id)
int i;
/* For set, set8, id->id may be 0 */
if (!id->id && !id->is_set && !id->is_set8)
if (!id->id && !id->is_set && !id->is_set8) {
pr_err("WARN: resolve_btfids: unresolved symbol %s\n", id->name);
warnings++;
}
for (i = 0; i < id->addr_cnt; i++) {
unsigned long addr = id->addr[i];
@ -782,6 +786,7 @@ int main(int argc, const char **argv)
.funcs = RB_ROOT,
.sets = RB_ROOT,
};
bool fatal_warnings = false;
struct option btfid_options[] = {
OPT_INCR('v', "verbose", &verbose,
"be more verbose (show errors, etc)"),
@ -789,6 +794,8 @@ int main(int argc, const char **argv)
"BTF data"),
OPT_STRING('b', "btf_base", &obj.base_btf_path, "file",
"path of file providing base BTF"),
OPT_BOOLEAN(0, "fatal_warnings", &fatal_warnings,
"turn warnings into errors"),
OPT_END()
};
int err = -1;
@ -823,7 +830,8 @@ int main(int argc, const char **argv)
if (symbols_patch(&obj))
goto out;
err = 0;
if (!(fatal_warnings && warnings))
err = 0;
out:
if (obj.efile.elf) {
elf_end(obj.efile.elf);

View File

@ -273,6 +273,16 @@
.off = OFF, \
.imm = 0 })
/* Unconditional jumps, gotol pc + imm32 */
#define BPF_JMP32_A(IMM) \
((struct bpf_insn) { \
.code = BPF_JMP32 | BPF_JA, \
.dst_reg = 0, \
.src_reg = 0, \
.off = 0, \
.imm = IMM })
/* Function call */
#define BPF_EMIT_CALL(FUNC) \

View File

@ -1573,6 +1573,16 @@ union bpf_attr {
* If provided, prog_flags should have BPF_F_TOKEN_FD flag set.
*/
__s32 prog_token_fd;
/* The fd_array_cnt can be used to pass the length of the
* fd_array array. In this case all the [map] file descriptors
* passed in this array will be bound to the program, even if
* the maps are not referenced directly. The functionality is
* similar to the BPF_PROG_BIND_MAP syscall, but maps can be
* used by the verifier during the program load. If provided,
* then the fd_array[0,...,fd_array_cnt-1] is expected to be
* continuous.
*/
__u32 fd_array_cnt;
};
struct { /* anonymous struct used by BPF_OBJ_* commands */

View File

@ -238,7 +238,7 @@ int bpf_prog_load(enum bpf_prog_type prog_type,
const struct bpf_insn *insns, size_t insn_cnt,
struct bpf_prog_load_opts *opts)
{
const size_t attr_sz = offsetofend(union bpf_attr, prog_token_fd);
const size_t attr_sz = offsetofend(union bpf_attr, fd_array_cnt);
void *finfo = NULL, *linfo = NULL;
const char *func_info, *line_info;
__u32 log_size, log_level, attach_prog_fd, attach_btf_obj_fd;
@ -311,6 +311,7 @@ int bpf_prog_load(enum bpf_prog_type prog_type,
attr.line_info_cnt = OPTS_GET(opts, line_info_cnt, 0);
attr.fd_array = ptr_to_u64(OPTS_GET(opts, fd_array, NULL));
attr.fd_array_cnt = OPTS_GET(opts, fd_array_cnt, 0);
if (log_level) {
attr.log_buf = ptr_to_u64(log_buf);

View File

@ -107,9 +107,12 @@ struct bpf_prog_load_opts {
*/
__u32 log_true_size;
__u32 token_fd;
/* if set, provides the length of fd_array */
__u32 fd_array_cnt;
size_t :0;
};
#define bpf_prog_load_opts__last_field token_fd
#define bpf_prog_load_opts__last_field fd_array_cnt
LIBBPF_API int bpf_prog_load(enum bpf_prog_type prog_type,
const char *prog_name, const char *license,

View File

@ -283,7 +283,7 @@ static int btf_parse_str_sec(struct btf *btf)
return -EINVAL;
}
if (!btf->base_btf && start[0]) {
pr_debug("Invalid BTF string section\n");
pr_debug("Malformed BTF string section, did you forget to provide base BTF?\n");
return -EINVAL;
}
return 0;

View File

@ -1796,9 +1796,14 @@ struct bpf_linker_file_opts {
struct bpf_linker;
LIBBPF_API struct bpf_linker *bpf_linker__new(const char *filename, struct bpf_linker_opts *opts);
LIBBPF_API struct bpf_linker *bpf_linker__new_fd(int fd, struct bpf_linker_opts *opts);
LIBBPF_API int bpf_linker__add_file(struct bpf_linker *linker,
const char *filename,
const struct bpf_linker_file_opts *opts);
LIBBPF_API int bpf_linker__add_fd(struct bpf_linker *linker, int fd,
const struct bpf_linker_file_opts *opts);
LIBBPF_API int bpf_linker__add_buf(struct bpf_linker *linker, void *buf, size_t buf_sz,
const struct bpf_linker_file_opts *opts);
LIBBPF_API int bpf_linker__finalize(struct bpf_linker *linker);
LIBBPF_API void bpf_linker__free(struct bpf_linker *linker);

View File

@ -432,4 +432,8 @@ LIBBPF_1.5.0 {
} LIBBPF_1.4.0;
LIBBPF_1.6.0 {
global:
bpf_linker__add_buf;
bpf_linker__add_fd;
bpf_linker__new_fd;
} LIBBPF_1.5.0;

View File

@ -4,6 +4,10 @@
*
* Copyright (c) 2021 Facebook
*/
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
@ -16,6 +20,7 @@
#include <elf.h>
#include <libelf.h>
#include <fcntl.h>
#include <sys/mman.h>
#include "libbpf.h"
#include "btf.h"
#include "libbpf_internal.h"
@ -152,15 +157,19 @@ struct bpf_linker {
/* global (including extern) ELF symbols */
int glob_sym_cnt;
struct glob_sym *glob_syms;
bool fd_is_owned;
};
#define pr_warn_elf(fmt, ...) \
libbpf_print(LIBBPF_WARN, "libbpf: " fmt ": %s\n", ##__VA_ARGS__, elf_errmsg(-1))
static int init_output_elf(struct bpf_linker *linker, const char *file);
static int init_output_elf(struct bpf_linker *linker);
static int linker_load_obj_file(struct bpf_linker *linker, const char *filename,
const struct bpf_linker_file_opts *opts,
static int bpf_linker_add_file(struct bpf_linker *linker, int fd,
const char *filename);
static int linker_load_obj_file(struct bpf_linker *linker,
struct src_obj *obj);
static int linker_sanity_check_elf(struct src_obj *obj);
static int linker_sanity_check_elf_symtab(struct src_obj *obj, struct src_sec *sec);
@ -191,7 +200,7 @@ void bpf_linker__free(struct bpf_linker *linker)
if (linker->elf)
elf_end(linker->elf);
if (linker->fd >= 0)
if (linker->fd >= 0 && linker->fd_is_owned)
close(linker->fd);
strset__free(linker->strtab_strs);
@ -233,9 +242,63 @@ struct bpf_linker *bpf_linker__new(const char *filename, struct bpf_linker_opts
if (!linker)
return errno = ENOMEM, NULL;
linker->fd = -1;
linker->filename = strdup(filename);
if (!linker->filename) {
err = -ENOMEM;
goto err_out;
}
err = init_output_elf(linker, filename);
linker->fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644);
if (linker->fd < 0) {
err = -errno;
pr_warn("failed to create '%s': %d\n", filename, err);
goto err_out;
}
linker->fd_is_owned = true;
err = init_output_elf(linker);
if (err)
goto err_out;
return linker;
err_out:
bpf_linker__free(linker);
return errno = -err, NULL;
}
struct bpf_linker *bpf_linker__new_fd(int fd, struct bpf_linker_opts *opts)
{
struct bpf_linker *linker;
char filename[32];
int err;
if (fd < 0)
return errno = EINVAL, NULL;
if (!OPTS_VALID(opts, bpf_linker_opts))
return errno = EINVAL, NULL;
if (elf_version(EV_CURRENT) == EV_NONE) {
pr_warn_elf("libelf initialization failed");
return errno = EINVAL, NULL;
}
linker = calloc(1, sizeof(*linker));
if (!linker)
return errno = ENOMEM, NULL;
snprintf(filename, sizeof(filename), "fd:%d", fd);
linker->filename = strdup(filename);
if (!linker->filename) {
err = -ENOMEM;
goto err_out;
}
linker->fd = fd;
linker->fd_is_owned = false;
err = init_output_elf(linker);
if (err)
goto err_out;
@ -294,23 +357,12 @@ static Elf64_Sym *add_new_sym(struct bpf_linker *linker, size_t *sym_idx)
return sym;
}
static int init_output_elf(struct bpf_linker *linker, const char *file)
static int init_output_elf(struct bpf_linker *linker)
{
int err, str_off;
Elf64_Sym *init_sym;
struct dst_sec *sec;
linker->filename = strdup(file);
if (!linker->filename)
return -ENOMEM;
linker->fd = open(file, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644);
if (linker->fd < 0) {
err = -errno;
pr_warn("failed to create '%s': %s\n", file, errstr(err));
return err;
}
linker->elf = elf_begin(linker->fd, ELF_C_WRITE, NULL);
if (!linker->elf) {
pr_warn_elf("failed to create ELF object");
@ -436,19 +488,16 @@ static int init_output_elf(struct bpf_linker *linker, const char *file)
return 0;
}
int bpf_linker__add_file(struct bpf_linker *linker, const char *filename,
const struct bpf_linker_file_opts *opts)
static int bpf_linker_add_file(struct bpf_linker *linker, int fd,
const char *filename)
{
struct src_obj obj = {};
int err = 0;
if (!OPTS_VALID(opts, bpf_linker_file_opts))
return libbpf_err(-EINVAL);
obj.filename = filename;
obj.fd = fd;
if (!linker->elf)
return libbpf_err(-EINVAL);
err = err ?: linker_load_obj_file(linker, filename, opts, &obj);
err = err ?: linker_load_obj_file(linker, &obj);
err = err ?: linker_append_sec_data(linker, &obj);
err = err ?: linker_append_elf_syms(linker, &obj);
err = err ?: linker_append_elf_relos(linker, &obj);
@ -463,12 +512,91 @@ int bpf_linker__add_file(struct bpf_linker *linker, const char *filename,
free(obj.sym_map);
if (obj.elf)
elf_end(obj.elf);
if (obj.fd >= 0)
close(obj.fd);
return err;
}
int bpf_linker__add_file(struct bpf_linker *linker, const char *filename,
const struct bpf_linker_file_opts *opts)
{
int fd, err;
if (!OPTS_VALID(opts, bpf_linker_file_opts))
return libbpf_err(-EINVAL);
if (!linker->elf)
return libbpf_err(-EINVAL);
fd = open(filename, O_RDONLY | O_CLOEXEC);
if (fd < 0) {
err = -errno;
pr_warn("failed to open file '%s': %s\n", filename, errstr(err));
return libbpf_err(err);
}
err = bpf_linker_add_file(linker, fd, filename);
close(fd);
return libbpf_err(err);
}
int bpf_linker__add_fd(struct bpf_linker *linker, int fd,
const struct bpf_linker_file_opts *opts)
{
char filename[32];
int err;
if (!OPTS_VALID(opts, bpf_linker_file_opts))
return libbpf_err(-EINVAL);
if (!linker->elf)
return libbpf_err(-EINVAL);
if (fd < 0)
return libbpf_err(-EINVAL);
snprintf(filename, sizeof(filename), "fd:%d", fd);
err = bpf_linker_add_file(linker, fd, filename);
return libbpf_err(err);
}
int bpf_linker__add_buf(struct bpf_linker *linker, void *buf, size_t buf_sz,
const struct bpf_linker_file_opts *opts)
{
char filename[32];
int fd, written, ret;
if (!OPTS_VALID(opts, bpf_linker_file_opts))
return libbpf_err(-EINVAL);
if (!linker->elf)
return libbpf_err(-EINVAL);
snprintf(filename, sizeof(filename), "mem:%p+%zu", buf, buf_sz);
fd = memfd_create(filename, 0);
if (fd < 0) {
ret = -errno;
pr_warn("failed to create memfd '%s': %s\n", filename, errstr(ret));
return libbpf_err(ret);
}
written = 0;
while (written < buf_sz) {
ret = write(fd, buf, buf_sz);
if (ret < 0) {
ret = -errno;
pr_warn("failed to write '%s': %s\n", filename, errstr(ret));
goto err_out;
}
written += ret;
}
ret = bpf_linker_add_file(linker, fd, filename);
err_out:
close(fd);
return libbpf_err(ret);
}
static bool is_dwarf_sec_name(const char *name)
{
/* approximation, but the actual list is too long */
@ -534,8 +662,7 @@ static struct src_sec *add_src_sec(struct src_obj *obj, const char *sec_name)
return sec;
}
static int linker_load_obj_file(struct bpf_linker *linker, const char *filename,
const struct bpf_linker_file_opts *opts,
static int linker_load_obj_file(struct bpf_linker *linker,
struct src_obj *obj)
{
int err = 0;
@ -554,36 +681,26 @@ static int linker_load_obj_file(struct bpf_linker *linker, const char *filename,
#error "Unknown __BYTE_ORDER__"
#endif
pr_debug("linker: adding object file '%s'...\n", filename);
pr_debug("linker: adding object file '%s'...\n", obj->filename);
obj->filename = filename;
obj->fd = open(filename, O_RDONLY | O_CLOEXEC);
if (obj->fd < 0) {
err = -errno;
pr_warn("failed to open file '%s': %s\n", filename, errstr(err));
return err;
}
obj->elf = elf_begin(obj->fd, ELF_C_READ_MMAP, NULL);
if (!obj->elf) {
err = -errno;
pr_warn_elf("failed to parse ELF file '%s'", filename);
return err;
pr_warn_elf("failed to parse ELF file '%s'", obj->filename);
return -EINVAL;
}
/* Sanity check ELF file high-level properties */
ehdr = elf64_getehdr(obj->elf);
if (!ehdr) {
err = -errno;
pr_warn_elf("failed to get ELF header for %s", filename);
return err;
pr_warn_elf("failed to get ELF header for %s", obj->filename);
return -EINVAL;
}
/* Linker output endianness set by first input object */
obj_byteorder = ehdr->e_ident[EI_DATA];
if (obj_byteorder != ELFDATA2LSB && obj_byteorder != ELFDATA2MSB) {
err = -EOPNOTSUPP;
pr_warn("unknown byte order of ELF file %s\n", filename);
pr_warn("unknown byte order of ELF file %s\n", obj->filename);
return err;
}
if (link_byteorder == ELFDATANONE) {
@ -593,7 +710,7 @@ static int linker_load_obj_file(struct bpf_linker *linker, const char *filename,
obj_byteorder == ELFDATA2MSB ? "big" : "little");
} else if (link_byteorder != obj_byteorder) {
err = -EOPNOTSUPP;
pr_warn("byte order mismatch with ELF file %s\n", filename);
pr_warn("byte order mismatch with ELF file %s\n", obj->filename);
return err;
}
@ -601,14 +718,13 @@ static int linker_load_obj_file(struct bpf_linker *linker, const char *filename,
|| ehdr->e_machine != EM_BPF
|| ehdr->e_ident[EI_CLASS] != ELFCLASS64) {
err = -EOPNOTSUPP;
pr_warn_elf("unsupported kind of ELF file %s", filename);
pr_warn_elf("unsupported kind of ELF file %s", obj->filename);
return err;
}
if (elf_getshdrstrndx(obj->elf, &obj->shstrs_sec_idx)) {
err = -errno;
pr_warn_elf("failed to get SHSTRTAB section index for %s", filename);
return err;
pr_warn_elf("failed to get SHSTRTAB section index for %s", obj->filename);
return -EINVAL;
}
scn = NULL;
@ -618,26 +734,23 @@ static int linker_load_obj_file(struct bpf_linker *linker, const char *filename,
shdr = elf64_getshdr(scn);
if (!shdr) {
err = -errno;
pr_warn_elf("failed to get section #%zu header for %s",
sec_idx, filename);
return err;
sec_idx, obj->filename);
return -EINVAL;
}
sec_name = elf_strptr(obj->elf, obj->shstrs_sec_idx, shdr->sh_name);
if (!sec_name) {
err = -errno;
pr_warn_elf("failed to get section #%zu name for %s",
sec_idx, filename);
return err;
sec_idx, obj->filename);
return -EINVAL;
}
data = elf_getdata(scn, 0);
if (!data) {
err = -errno;
pr_warn_elf("failed to get section #%zu (%s) data from %s",
sec_idx, sec_name, filename);
return err;
sec_idx, sec_name, obj->filename);
return -EINVAL;
}
sec = add_src_sec(obj, sec_name);
@ -672,7 +785,7 @@ static int linker_load_obj_file(struct bpf_linker *linker, const char *filename,
err = libbpf_get_error(obj->btf);
if (err) {
pr_warn("failed to parse .BTF from %s: %s\n",
filename, errstr(err));
obj->filename, errstr(err));
return err;
}
sec->skipped = true;
@ -683,7 +796,7 @@ static int linker_load_obj_file(struct bpf_linker *linker, const char *filename,
err = libbpf_get_error(obj->btf_ext);
if (err) {
pr_warn("failed to parse .BTF.ext from '%s': %s\n",
filename, errstr(err));
obj->filename, errstr(err));
return err;
}
sec->skipped = true;
@ -700,7 +813,7 @@ static int linker_load_obj_file(struct bpf_linker *linker, const char *filename,
break;
default:
pr_warn("unrecognized section #%zu (%s) in %s\n",
sec_idx, sec_name, filename);
sec_idx, sec_name, obj->filename);
err = -EINVAL;
return err;
}
@ -2680,22 +2793,23 @@ int bpf_linker__finalize(struct bpf_linker *linker)
/* Finalize ELF layout */
if (elf_update(linker->elf, ELF_C_NULL) < 0) {
err = -errno;
err = -EINVAL;
pr_warn_elf("failed to finalize ELF layout");
return libbpf_err(err);
}
/* Write out final ELF contents */
if (elf_update(linker->elf, ELF_C_WRITE) < 0) {
err = -errno;
err = -EINVAL;
pr_warn_elf("failed to write ELF contents");
return libbpf_err(err);
}
elf_end(linker->elf);
close(linker->fd);
linker->elf = NULL;
if (linker->fd_is_owned)
close(linker->fd);
linker->fd = -1;
return 0;

View File

@ -661,7 +661,7 @@ static int collect_usdt_targets(struct usdt_manager *man, Elf *elf, const char *
* [0] https://sourceware.org/systemtap/wiki/UserSpaceProbeImplementation
*/
usdt_abs_ip = note.loc_addr;
if (base_addr)
if (base_addr && note.base_addr)
usdt_abs_ip += base_addr - note.base_addr;
/* When attaching uprobes (which is what USDTs basically are)

View File

@ -18,7 +18,6 @@ feature
urandom_read
test_sockmap
test_lirc_mode2_user
test_flow_dissector
flow_dissector_load
test_tcpnotify_user
test_libbpf

View File

@ -129,11 +129,9 @@ TEST_FILES = xsk_prereqs.sh $(wildcard progs/btf_dump_test_case_*.c)
TEST_PROGS := test_kmod.sh \
test_xdp_redirect.sh \
test_xdp_redirect_multi.sh \
test_xdp_meta.sh \
test_tunnel.sh \
test_lwt_seg6local.sh \
test_lirc_mode2.sh \
test_flow_dissector.sh \
test_xdp_vlan_mode_generic.sh \
test_xdp_vlan_mode_native.sh \
test_lwt_ip_encap.sh \
@ -151,17 +149,16 @@ TEST_PROGS_EXTENDED := with_addr.sh \
with_tunnels.sh ima_setup.sh verify_sig_setup.sh \
test_xdp_vlan.sh test_bpftool.py
TEST_KMODS := bpf_testmod.ko bpf_test_no_cfi.ko bpf_test_modorder_x.ko \
bpf_test_modorder_y.ko
TEST_KMOD_TARGETS = $(addprefix $(OUTPUT)/,$(TEST_KMODS))
# Compile but not part of 'make run_tests'
TEST_GEN_PROGS_EXTENDED = \
bench \
bpf_testmod.ko \
bpf_test_modorder_x.ko \
bpf_test_modorder_y.ko \
bpf_test_no_cfi.ko \
flow_dissector_load \
runqslower \
test_cpp \
test_flow_dissector \
test_lirc_mode2_user \
veristat \
xdp_features \
@ -184,8 +181,9 @@ override define CLEAN
$(Q)$(RM) -r $(TEST_GEN_PROGS)
$(Q)$(RM) -r $(TEST_GEN_PROGS_EXTENDED)
$(Q)$(RM) -r $(TEST_GEN_FILES)
$(Q)$(RM) -r $(TEST_KMODS)
$(Q)$(RM) -r $(EXTRA_CLEAN)
$(Q)$(MAKE) -C bpf_testmod clean
$(Q)$(MAKE) -C test_kmods clean
$(Q)$(MAKE) docs-clean
endef
@ -251,7 +249,7 @@ endif
# to build individual tests.
# NOTE: Semicolon at the end is critical to override lib.mk's default static
# rule for binaries.
$(notdir $(TEST_GEN_PROGS) \
$(notdir $(TEST_GEN_PROGS) $(TEST_KMODS) \
$(TEST_GEN_PROGS_EXTENDED)): %: $(OUTPUT)/% ;
# sort removes libbpf duplicates when not cross-building
@ -305,37 +303,19 @@ $(OUTPUT)/sign-file: ../../../../scripts/sign-file.c
$< -o $@ \
$(shell $(PKG_CONFIG) --libs libcrypto 2> /dev/null || echo -lcrypto)
$(OUTPUT)/bpf_testmod.ko: $(VMLINUX_BTF) $(RESOLVE_BTFIDS) $(wildcard bpf_testmod/Makefile bpf_testmod/*.[ch])
$(call msg,MOD,,$@)
$(Q)$(RM) bpf_testmod/bpf_testmod.ko # force re-compilation
$(Q)$(MAKE) $(submake_extras) -C bpf_testmod \
RESOLVE_BTFIDS=$(RESOLVE_BTFIDS) \
# This should really be a grouped target, but make versions before 4.3 don't
# support that for regular rules. However, pattern matching rules are implicitly
# treated as grouped even with older versions of make, so as a workaround, the
# subst() turns the rule into a pattern matching rule
$(addprefix test_kmods/,$(subst .ko,%ko,$(TEST_KMODS))): $(VMLINUX_BTF) $(RESOLVE_BTFIDS) $(wildcard test_kmods/Makefile test_kmods/*.[ch])
$(Q)$(RM) test_kmods/*.ko test_kmods/*.mod.o # force re-compilation
$(Q)$(MAKE) $(submake_extras) -C test_kmods \
RESOLVE_BTFIDS=$(RESOLVE_BTFIDS) \
EXTRA_CFLAGS='' EXTRA_LDFLAGS=''
$(Q)cp bpf_testmod/bpf_testmod.ko $@
$(OUTPUT)/bpf_test_no_cfi.ko: $(VMLINUX_BTF) $(RESOLVE_BTFIDS) $(wildcard bpf_test_no_cfi/Makefile bpf_test_no_cfi/*.[ch])
$(TEST_KMOD_TARGETS): $(addprefix test_kmods/,$(TEST_KMODS))
$(call msg,MOD,,$@)
$(Q)$(RM) bpf_test_no_cfi/bpf_test_no_cfi.ko # force re-compilation
$(Q)$(MAKE) $(submake_extras) -C bpf_test_no_cfi \
RESOLVE_BTFIDS=$(RESOLVE_BTFIDS) \
EXTRA_CFLAGS='' EXTRA_LDFLAGS=''
$(Q)cp bpf_test_no_cfi/bpf_test_no_cfi.ko $@
$(OUTPUT)/bpf_test_modorder_x.ko: $(VMLINUX_BTF) $(RESOLVE_BTFIDS) $(wildcard bpf_test_modorder_x/Makefile bpf_test_modorder_x/*.[ch])
$(call msg,MOD,,$@)
$(Q)$(RM) bpf_test_modorder_x/bpf_test_modorder_x.ko # force re-compilation
$(Q)$(MAKE) $(submake_extras) -C bpf_test_modorder_x \
RESOLVE_BTFIDS=$(RESOLVE_BTFIDS) \
EXTRA_CFLAGS='' EXTRA_LDFLAGS=''
$(Q)cp bpf_test_modorder_x/bpf_test_modorder_x.ko $@
$(OUTPUT)/bpf_test_modorder_y.ko: $(VMLINUX_BTF) $(RESOLVE_BTFIDS) $(wildcard bpf_test_modorder_y/Makefile bpf_test_modorder_y/*.[ch])
$(call msg,MOD,,$@)
$(Q)$(RM) bpf_test_modorder_y/bpf_test_modorder_y.ko # force re-compilation
$(Q)$(MAKE) $(submake_extras) -C bpf_test_modorder_y \
RESOLVE_BTFIDS=$(RESOLVE_BTFIDS) \
EXTRA_CFLAGS='' EXTRA_LDFLAGS=''
$(Q)cp bpf_test_modorder_y/bpf_test_modorder_y.ko $@
$(Q)cp test_kmods/$(@F) $@
DEFAULT_BPFTOOL := $(HOST_SCRATCH_DIR)/sbin/bpftool
@ -480,10 +460,10 @@ $(shell $(1) $(2) -dM -E - </dev/null | grep -E 'MIPS(EL|EB)|_MIPS_SZ(PTR|LONG)
endef
# Determine target endianness.
IS_LITTLE_ENDIAN = $(shell $(CC) -dM -E - </dev/null | \
IS_LITTLE_ENDIAN := $(shell $(CC) -dM -E - </dev/null | \
grep 'define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__')
MENDIAN=$(if $(IS_LITTLE_ENDIAN),-mlittle-endian,-mbig-endian)
BPF_TARGET_ENDIAN=$(if $(IS_LITTLE_ENDIAN),--target=bpfel,--target=bpfeb)
MENDIAN:=$(if $(IS_LITTLE_ENDIAN),-mlittle-endian,-mbig-endian)
BPF_TARGET_ENDIAN:=$(if $(IS_LITTLE_ENDIAN),--target=bpfel,--target=bpfeb)
ifneq ($(CROSS_COMPILE),)
CLANG_TARGET_ARCH = --target=$(notdir $(CROSS_COMPILE:%-=%))
@ -760,14 +740,12 @@ TRUNNER_EXTRA_SOURCES := test_progs.c \
json_writer.c \
flow_dissector_load.h \
ip_check_defrag_frags.h
TRUNNER_EXTRA_FILES := $(OUTPUT)/urandom_read $(OUTPUT)/bpf_testmod.ko \
$(OUTPUT)/bpf_test_no_cfi.ko \
$(OUTPUT)/bpf_test_modorder_x.ko \
$(OUTPUT)/bpf_test_modorder_y.ko \
TRUNNER_EXTRA_FILES := $(OUTPUT)/urandom_read \
$(OUTPUT)/liburandom_read.so \
$(OUTPUT)/xdp_synproxy \
$(OUTPUT)/sign-file \
$(OUTPUT)/uprobe_multi \
$(TEST_KMOD_TARGETS) \
ima_setup.sh \
verify_sig_setup.sh \
$(wildcard progs/btf_dump_test_case_*.c) \
@ -894,12 +872,9 @@ $(OUTPUT)/uprobe_multi: uprobe_multi.c uprobe_multi.ld
EXTRA_CLEAN := $(SCRATCH_DIR) $(HOST_SCRATCH_DIR) \
prog_tests/tests.h map_tests/tests.h verifier/tests.h \
feature bpftool \
feature bpftool $(TEST_KMOD_TARGETS) \
$(addprefix $(OUTPUT)/,*.o *.d *.skel.h *.lskel.h *.subskel.h \
no_alu32 cpuv4 bpf_gcc bpf_testmod.ko \
bpf_test_no_cfi.ko \
bpf_test_modorder_x.ko \
bpf_test_modorder_y.ko \
no_alu32 cpuv4 bpf_gcc \
liburandom_read.so) \
$(OUTPUT)/FEATURE-DUMP.selftests

View File

@ -1,19 +0,0 @@
BPF_TESTMOD_DIR := $(realpath $(dir $(abspath $(lastword $(MAKEFILE_LIST)))))
KDIR ?= $(abspath $(BPF_TESTMOD_DIR)/../../../../..)
ifeq ($(V),1)
Q =
else
Q = @
endif
MODULES = bpf_test_modorder_x.ko
obj-m += bpf_test_modorder_x.o
all:
+$(Q)make -C $(KDIR) M=$(BPF_TESTMOD_DIR) modules
clean:
+$(Q)make -C $(KDIR) M=$(BPF_TESTMOD_DIR) clean

View File

@ -1,19 +0,0 @@
BPF_TESTMOD_DIR := $(realpath $(dir $(abspath $(lastword $(MAKEFILE_LIST)))))
KDIR ?= $(abspath $(BPF_TESTMOD_DIR)/../../../../..)
ifeq ($(V),1)
Q =
else
Q = @
endif
MODULES = bpf_test_modorder_y.ko
obj-m += bpf_test_modorder_y.o
all:
+$(Q)make -C $(KDIR) M=$(BPF_TESTMOD_DIR) modules
clean:
+$(Q)make -C $(KDIR) M=$(BPF_TESTMOD_DIR) clean

View File

@ -1,19 +0,0 @@
BPF_TEST_NO_CFI_DIR := $(realpath $(dir $(abspath $(lastword $(MAKEFILE_LIST)))))
KDIR ?= $(abspath $(BPF_TEST_NO_CFI_DIR)/../../../../..)
ifeq ($(V),1)
Q =
else
Q = @
endif
MODULES = bpf_test_no_cfi.ko
obj-m += bpf_test_no_cfi.o
all:
+$(Q)make -C $(KDIR) M=$(BPF_TEST_NO_CFI_DIR) modules
clean:
+$(Q)make -C $(KDIR) M=$(BPF_TEST_NO_CFI_DIR) clean

View File

@ -1,20 +0,0 @@
BPF_TESTMOD_DIR := $(realpath $(dir $(abspath $(lastword $(MAKEFILE_LIST)))))
KDIR ?= $(abspath $(BPF_TESTMOD_DIR)/../../../../..)
ifeq ($(V),1)
Q =
else
Q = @
endif
MODULES = bpf_testmod.ko
obj-m += bpf_testmod.o
CFLAGS_bpf_testmod.o = -I$(src)
all:
+$(Q)make -C $(KDIR) M=$(BPF_TESTMOD_DIR) modules
clean:
+$(Q)make -C $(KDIR) M=$(BPF_TESTMOD_DIR) clean

View File

@ -58,6 +58,7 @@ CONFIG_MPLS=y
CONFIG_MPLS_IPTUNNEL=y
CONFIG_MPLS_ROUTING=y
CONFIG_MPTCP=y
CONFIG_NET_ACT_GACT=y
CONFIG_NET_ACT_SKBMOD=y
CONFIG_NET_CLS=y
CONFIG_NET_CLS_ACT=y

View File

@ -21,7 +21,7 @@
#include <linux/limits.h>
#include <linux/ip.h>
#include <linux/udp.h>
#include <netinet/udp.h>
#include <netinet/tcp.h>
#include <net/if.h>

View File

@ -14,6 +14,7 @@ typedef __u16 __sum16;
#include <linux/sockios.h>
#include <linux/err.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <bpf/bpf_endian.h>
#include <net/if.h>
@ -105,6 +106,45 @@ static __u16 csum_fold(__u32 csum)
return (__u16)~csum;
}
static __wsum csum_partial(const void *buf, int len, __wsum sum)
{
__u16 *p = (__u16 *)buf;
int num_u16 = len >> 1;
int i;
for (i = 0; i < num_u16; i++)
sum += p[i];
return sum;
}
static inline __sum16 build_ip_csum(struct iphdr *iph)
{
__u32 sum = 0;
__u16 *p;
iph->check = 0;
p = (void *)iph;
sum = csum_partial(p, iph->ihl << 2, 0);
return csum_fold(sum);
}
/**
* csum_tcpudp_magic - compute IP pseudo-header checksum
*
* Compute the IPv4 pseudo header checksum. The helper can take a
* accumulated sum from the transport layer to accumulate it and directly
* return the transport layer
*
* @saddr: IP source address
* @daddr: IP dest address
* @len: IP data size
* @proto: transport layer protocol
* @csum: The accumulated partial sum to add to the computation
*
* Returns the folded sum
*/
static inline __sum16 csum_tcpudp_magic(__be32 saddr, __be32 daddr,
__u32 len, __u8 proto,
__wsum csum)
@ -120,6 +160,21 @@ static inline __sum16 csum_tcpudp_magic(__be32 saddr, __be32 daddr,
return csum_fold((__u32)s);
}
/**
* csum_ipv6_magic - compute IPv6 pseudo-header checksum
*
* Compute the ipv6 pseudo header checksum. The helper can take a
* accumulated sum from the transport layer to accumulate it and directly
* return the transport layer
*
* @saddr: IPv6 source address
* @daddr: IPv6 dest address
* @len: IPv6 data size
* @proto: transport layer protocol
* @csum: The accumulated partial sum to add to the computation
*
* Returns the folded sum
*/
static inline __sum16 csum_ipv6_magic(const struct in6_addr *saddr,
const struct in6_addr *daddr,
__u32 len, __u8 proto,
@ -139,6 +194,47 @@ static inline __sum16 csum_ipv6_magic(const struct in6_addr *saddr,
return csum_fold((__u32)s);
}
/**
* build_udp_v4_csum - compute UDP checksum for UDP over IPv4
*
* Compute the checksum to embed in UDP header, composed of the sum of IP
* pseudo-header checksum, UDP header checksum and UDP data checksum
* @iph IP header
* @udph UDP header, which must be immediately followed by UDP data
*
* Returns the total checksum
*/
static inline __sum16 build_udp_v4_csum(const struct iphdr *iph,
const struct udphdr *udph)
{
unsigned long sum;
sum = csum_partial(udph, ntohs(udph->len), 0);
return csum_tcpudp_magic(iph->saddr, iph->daddr, ntohs(udph->len),
IPPROTO_UDP, sum);
}
/**
* build_udp_v6_csum - compute UDP checksum for UDP over IPv6
*
* Compute the checksum to embed in UDP header, composed of the sum of IPv6
* pseudo-header checksum, UDP header checksum and UDP data checksum
* @ip6h IPv6 header
* @udph UDP header, which must be immediately followed by UDP data
*
* Returns the total checksum
*/
static inline __sum16 build_udp_v6_csum(const struct ipv6hdr *ip6h,
const struct udphdr *udph)
{
unsigned long sum;
sum = csum_partial(udph, ntohs(udph->len), 0);
return csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, ntohs(udph->len),
IPPROTO_UDP, sum);
}
struct tmonitor_ctx;
#ifdef TRAFFIC_MONITOR

View File

@ -0,0 +1,28 @@
// SPDX-License-Identifier: GPL-2.0
#include <test_progs.h>
#include "cgroup_skb_direct_packet_access.skel.h"
void test_cgroup_skb_prog_run_direct_packet_access(void)
{
int err;
struct cgroup_skb_direct_packet_access *skel;
char test_skb[64] = {};
LIBBPF_OPTS(bpf_test_run_opts, topts,
.data_in = test_skb,
.data_size_in = sizeof(test_skb),
);
skel = cgroup_skb_direct_packet_access__open_and_load();
if (!ASSERT_OK_PTR(skel, "cgroup_skb_direct_packet_access__open_and_load"))
return;
err = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.direct_packet_access), &topts);
ASSERT_OK(err, "bpf_prog_test_run_opts err");
ASSERT_EQ(topts.retval, 1, "retval");
ASSERT_NEQ(skel->bss->data_end, 0, "data_end");
cgroup_skb_direct_packet_access__destroy(skel);
}

View File

@ -2,7 +2,7 @@
#define _GNU_SOURCE
#include <test_progs.h>
#include "progs/core_reloc_types.h"
#include "bpf_testmod/bpf_testmod.h"
#include "test_kmods/bpf_testmod.h"
#include <linux/limits.h>
#include <sys/mman.h>
#include <sys/syscall.h>

View File

@ -0,0 +1,441 @@
// SPDX-License-Identifier: GPL-2.0
#include <test_progs.h>
#include <linux/btf.h>
#include <bpf/bpf.h>
#include "../test_btf.h"
static inline int new_map(void)
{
const char *name = NULL;
__u32 max_entries = 1;
__u32 value_size = 8;
__u32 key_size = 4;
return bpf_map_create(BPF_MAP_TYPE_ARRAY, name,
key_size, value_size,
max_entries, NULL);
}
static int new_btf(void)
{
struct btf_blob {
struct btf_header btf_hdr;
__u32 types[8];
__u32 str;
} raw_btf = {
.btf_hdr = {
.magic = BTF_MAGIC,
.version = BTF_VERSION,
.hdr_len = sizeof(struct btf_header),
.type_len = sizeof(raw_btf.types),
.str_off = offsetof(struct btf_blob, str) - offsetof(struct btf_blob, types),
.str_len = sizeof(raw_btf.str),
},
.types = {
/* long */
BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 64, 8), /* [1] */
/* unsigned long */
BTF_TYPE_INT_ENC(0, 0, 0, 64, 8), /* [2] */
},
};
return bpf_btf_load(&raw_btf, sizeof(raw_btf), NULL);
}
#define Close(FD) do { \
if ((FD) >= 0) { \
close(FD); \
FD = -1; \
} \
} while(0)
static bool map_exists(__u32 id)
{
int fd;
fd = bpf_map_get_fd_by_id(id);
if (fd >= 0) {
close(fd);
return true;
}
return false;
}
static bool btf_exists(__u32 id)
{
int fd;
fd = bpf_btf_get_fd_by_id(id);
if (fd >= 0) {
close(fd);
return true;
}
return false;
}
static inline int bpf_prog_get_map_ids(int prog_fd, __u32 *nr_map_ids, __u32 *map_ids)
{
__u32 len = sizeof(struct bpf_prog_info);
struct bpf_prog_info info;
int err;
memset(&info, 0, len);
info.nr_map_ids = *nr_map_ids,
info.map_ids = ptr_to_u64(map_ids),
err = bpf_prog_get_info_by_fd(prog_fd, &info, &len);
if (!ASSERT_OK(err, "bpf_prog_get_info_by_fd"))
return -1;
*nr_map_ids = info.nr_map_ids;
return 0;
}
static int __load_test_prog(int map_fd, const int *fd_array, int fd_array_cnt)
{
/* A trivial program which uses one map */
struct bpf_insn insns[] = {
BPF_LD_MAP_FD(BPF_REG_1, map_fd),
BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
BPF_MOV64_IMM(BPF_REG_0, 0),
BPF_EXIT_INSN(),
};
LIBBPF_OPTS(bpf_prog_load_opts, opts);
opts.fd_array = fd_array;
opts.fd_array_cnt = fd_array_cnt;
return bpf_prog_load(BPF_PROG_TYPE_XDP, NULL, "GPL", insns, ARRAY_SIZE(insns), &opts);
}
static int load_test_prog(const int *fd_array, int fd_array_cnt)
{
int map_fd;
int ret;
map_fd = new_map();
if (!ASSERT_GE(map_fd, 0, "new_map"))
return map_fd;
ret = __load_test_prog(map_fd, fd_array, fd_array_cnt);
close(map_fd);
return ret;
}
static bool check_expected_map_ids(int prog_fd, int expected, __u32 *map_ids, __u32 *nr_map_ids)
{
int err;
err = bpf_prog_get_map_ids(prog_fd, nr_map_ids, map_ids);
if (!ASSERT_OK(err, "bpf_prog_get_map_ids"))
return false;
if (!ASSERT_EQ(*nr_map_ids, expected, "unexpected nr_map_ids"))
return false;
return true;
}
/*
* Load a program, which uses one map. No fd_array maps are present.
* On return only one map is expected to be bound to prog.
*/
static void check_fd_array_cnt__no_fd_array(void)
{
__u32 map_ids[16];
__u32 nr_map_ids;
int prog_fd = -1;
prog_fd = load_test_prog(NULL, 0);
if (!ASSERT_GE(prog_fd, 0, "BPF_PROG_LOAD"))
return;
nr_map_ids = ARRAY_SIZE(map_ids);
check_expected_map_ids(prog_fd, 1, map_ids, &nr_map_ids);
close(prog_fd);
}
/*
* Load a program, which uses one map, and pass two extra, non-equal, maps in
* fd_array with fd_array_cnt=2. On return three maps are expected to be bound
* to the program.
*/
static void check_fd_array_cnt__fd_array_ok(void)
{
int extra_fds[2] = { -1, -1 };
__u32 map_ids[16];
__u32 nr_map_ids;
int prog_fd = -1;
extra_fds[0] = new_map();
if (!ASSERT_GE(extra_fds[0], 0, "new_map"))
goto cleanup;
extra_fds[1] = new_map();
if (!ASSERT_GE(extra_fds[1], 0, "new_map"))
goto cleanup;
prog_fd = load_test_prog(extra_fds, 2);
if (!ASSERT_GE(prog_fd, 0, "BPF_PROG_LOAD"))
goto cleanup;
nr_map_ids = ARRAY_SIZE(map_ids);
if (!check_expected_map_ids(prog_fd, 3, map_ids, &nr_map_ids))
goto cleanup;
/* maps should still exist when original file descriptors are closed */
Close(extra_fds[0]);
Close(extra_fds[1]);
if (!ASSERT_EQ(map_exists(map_ids[0]), true, "map_ids[0] should exist"))
goto cleanup;
if (!ASSERT_EQ(map_exists(map_ids[1]), true, "map_ids[1] should exist"))
goto cleanup;
/* some fds might be invalid, so ignore return codes */
cleanup:
Close(extra_fds[1]);
Close(extra_fds[0]);
Close(prog_fd);
}
/*
* Load a program with a few extra maps duplicated in the fd_array.
* After the load maps should only be referenced once.
*/
static void check_fd_array_cnt__duplicated_maps(void)
{
int extra_fds[4] = { -1, -1, -1, -1 };
__u32 map_ids[16];
__u32 nr_map_ids;
int prog_fd = -1;
extra_fds[0] = extra_fds[2] = new_map();
if (!ASSERT_GE(extra_fds[0], 0, "new_map"))
goto cleanup;
extra_fds[1] = extra_fds[3] = new_map();
if (!ASSERT_GE(extra_fds[1], 0, "new_map"))
goto cleanup;
prog_fd = load_test_prog(extra_fds, 4);
if (!ASSERT_GE(prog_fd, 0, "BPF_PROG_LOAD"))
goto cleanup;
nr_map_ids = ARRAY_SIZE(map_ids);
if (!check_expected_map_ids(prog_fd, 3, map_ids, &nr_map_ids))
goto cleanup;
/* maps should still exist when original file descriptors are closed */
Close(extra_fds[0]);
Close(extra_fds[1]);
if (!ASSERT_EQ(map_exists(map_ids[0]), true, "map should exist"))
goto cleanup;
if (!ASSERT_EQ(map_exists(map_ids[1]), true, "map should exist"))
goto cleanup;
/* some fds might be invalid, so ignore return codes */
cleanup:
Close(extra_fds[1]);
Close(extra_fds[0]);
Close(prog_fd);
}
/*
* Check that if maps which are referenced by a program are
* passed in fd_array, then they will be referenced only once
*/
static void check_fd_array_cnt__referenced_maps_in_fd_array(void)
{
int extra_fds[1] = { -1 };
__u32 map_ids[16];
__u32 nr_map_ids;
int prog_fd = -1;
extra_fds[0] = new_map();
if (!ASSERT_GE(extra_fds[0], 0, "new_map"))
goto cleanup;
prog_fd = __load_test_prog(extra_fds[0], extra_fds, 1);
if (!ASSERT_GE(prog_fd, 0, "BPF_PROG_LOAD"))
goto cleanup;
nr_map_ids = ARRAY_SIZE(map_ids);
if (!check_expected_map_ids(prog_fd, 1, map_ids, &nr_map_ids))
goto cleanup;
/* map should still exist when original file descriptor is closed */
Close(extra_fds[0]);
if (!ASSERT_EQ(map_exists(map_ids[0]), true, "map should exist"))
goto cleanup;
/* some fds might be invalid, so ignore return codes */
cleanup:
Close(extra_fds[0]);
Close(prog_fd);
}
static int get_btf_id_by_fd(int btf_fd, __u32 *id)
{
struct bpf_btf_info info;
__u32 info_len = sizeof(info);
int err;
memset(&info, 0, info_len);
err = bpf_btf_get_info_by_fd(btf_fd, &info, &info_len);
if (err)
return err;
if (id)
*id = info.id;
return 0;
}
/*
* Check that fd_array operates properly for btfs. Namely, to check that
* passing a btf fd in fd_array increases its reference count, do the
* following:
* 1) Create a new btf, it's referenced only by a file descriptor, so refcnt=1
* 2) Load a BPF prog with fd_array[0] = btf_fd; now btf's refcnt=2
* 3) Close the btf_fd, now refcnt=1
* Wait and check that BTF stil exists.
*/
static void check_fd_array_cnt__referenced_btfs(void)
{
int extra_fds[1] = { -1 };
int prog_fd = -1;
__u32 btf_id;
int tries;
int err;
extra_fds[0] = new_btf();
if (!ASSERT_GE(extra_fds[0], 0, "new_btf"))
goto cleanup;
prog_fd = load_test_prog(extra_fds, 1);
if (!ASSERT_GE(prog_fd, 0, "BPF_PROG_LOAD"))
goto cleanup;
/* btf should still exist when original file descriptor is closed */
err = get_btf_id_by_fd(extra_fds[0], &btf_id);
if (!ASSERT_GE(err, 0, "get_btf_id_by_fd"))
goto cleanup;
Close(extra_fds[0]);
if (!ASSERT_GE(kern_sync_rcu(), 0, "kern_sync_rcu 1"))
goto cleanup;
if (!ASSERT_EQ(btf_exists(btf_id), true, "btf should exist"))
goto cleanup;
Close(prog_fd);
/* The program is freed by a workqueue, so no reliable
* way to sync, so just wait a bit (max ~1 second). */
for (tries = 100; tries >= 0; tries--) {
usleep(1000);
if (!btf_exists(btf_id))
break;
if (tries)
continue;
PRINT_FAIL("btf should have been freed");
}
/* some fds might be invalid, so ignore return codes */
cleanup:
Close(extra_fds[0]);
Close(prog_fd);
}
/*
* Test that a program with trash in fd_array can't be loaded:
* only map and BTF file descriptors should be accepted.
*/
static void check_fd_array_cnt__fd_array_with_trash(void)
{
int extra_fds[3] = { -1, -1, -1 };
int prog_fd = -1;
extra_fds[0] = new_map();
if (!ASSERT_GE(extra_fds[0], 0, "new_map"))
goto cleanup;
extra_fds[1] = new_btf();
if (!ASSERT_GE(extra_fds[1], 0, "new_btf"))
goto cleanup;
/* trash 1: not a file descriptor */
extra_fds[2] = 0xbeef;
prog_fd = load_test_prog(extra_fds, 3);
if (!ASSERT_EQ(prog_fd, -EBADF, "prog should have been rejected with -EBADF"))
goto cleanup;
/* trash 2: not a map or btf */
extra_fds[2] = socket(AF_INET, SOCK_STREAM, 0);
if (!ASSERT_GE(extra_fds[2], 0, "socket"))
goto cleanup;
prog_fd = load_test_prog(extra_fds, 3);
if (!ASSERT_EQ(prog_fd, -EINVAL, "prog should have been rejected with -EINVAL"))
goto cleanup;
/* Validate that the prog is ok if trash is removed */
Close(extra_fds[2]);
extra_fds[2] = new_btf();
if (!ASSERT_GE(extra_fds[2], 0, "new_btf"))
goto cleanup;
prog_fd = load_test_prog(extra_fds, 3);
if (!ASSERT_GE(prog_fd, 0, "prog should have been loaded"))
goto cleanup;
/* some fds might be invalid, so ignore return codes */
cleanup:
Close(extra_fds[2]);
Close(extra_fds[1]);
Close(extra_fds[0]);
}
/*
* Test that a program with too big fd_array can't be loaded.
*/
static void check_fd_array_cnt__fd_array_too_big(void)
{
int extra_fds[65];
int prog_fd = -1;
int i;
for (i = 0; i < 65; i++) {
extra_fds[i] = new_map();
if (!ASSERT_GE(extra_fds[i], 0, "new_map"))
goto cleanup_fds;
}
prog_fd = load_test_prog(extra_fds, 65);
ASSERT_EQ(prog_fd, -E2BIG, "prog should have been rejected with -E2BIG");
cleanup_fds:
while (i > 0)
Close(extra_fds[--i]);
}
void test_fd_array_cnt(void)
{
if (test__start_subtest("no-fd-array"))
check_fd_array_cnt__no_fd_array();
if (test__start_subtest("fd-array-ok"))
check_fd_array_cnt__fd_array_ok();
if (test__start_subtest("fd-array-dup-input"))
check_fd_array_cnt__duplicated_maps();
if (test__start_subtest("fd-array-ref-maps-in-array"))
check_fd_array_cnt__referenced_maps_in_fd_array();
if (test__start_subtest("fd-array-ref-btfs"))
check_fd_array_cnt__referenced_btfs();
if (test__start_subtest("fd-array-trash-input"))
check_fd_array_cnt__fd_array_with_trash();
if (test__start_subtest("fd-array-2big"))
check_fd_array_cnt__fd_array_too_big();
}

View File

@ -171,6 +171,10 @@ static void test_kprobe_fill_link_info(struct test_fill_link_info *skel,
/* See also arch_adjust_kprobe_addr(). */
if (skel->kconfig->CONFIG_X86_KERNEL_IBT)
entry_offset = 4;
if (skel->kconfig->CONFIG_PPC64 &&
skel->kconfig->CONFIG_KPROBES_ON_FTRACE &&
!skel->kconfig->CONFIG_PPC_FTRACE_OUT_OF_LINE)
entry_offset = 4;
err = verify_perf_link_info(link_fd, type, kprobe_addr, 0, entry_offset);
ASSERT_OK(err, "verify_perf_link_info");
} else {

View File

@ -7,39 +7,14 @@
#include "bpf_flow.skel.h"
#define TEST_NS "flow_dissector_ns"
#define FLOW_CONTINUE_SADDR 0x7f00007f /* 127.0.0.127 */
#define TEST_NAME_MAX_LEN 64
#ifndef IP_MF
#define IP_MF 0x2000
#endif
#define CHECK_FLOW_KEYS(desc, got, expected) \
_CHECK(memcmp(&got, &expected, sizeof(got)) != 0, \
desc, \
topts.duration, \
"nhoff=%u/%u " \
"thoff=%u/%u " \
"addr_proto=0x%x/0x%x " \
"is_frag=%u/%u " \
"is_first_frag=%u/%u " \
"is_encap=%u/%u " \
"ip_proto=0x%x/0x%x " \
"n_proto=0x%x/0x%x " \
"flow_label=0x%x/0x%x " \
"sport=%u/%u " \
"dport=%u/%u\n", \
got.nhoff, expected.nhoff, \
got.thoff, expected.thoff, \
got.addr_proto, expected.addr_proto, \
got.is_frag, expected.is_frag, \
got.is_first_frag, expected.is_first_frag, \
got.is_encap, expected.is_encap, \
got.ip_proto, expected.ip_proto, \
got.n_proto, expected.n_proto, \
got.flow_label, expected.flow_label, \
got.sport, expected.sport, \
got.dport, expected.dport)
struct ipv4_pkt {
struct ethhdr eth;
struct iphdr iph;
@ -89,6 +64,19 @@ struct dvlan_ipv6_pkt {
struct tcphdr tcp;
} __packed;
struct gre_base_hdr {
__be16 flags;
__be16 protocol;
} gre_base_hdr;
struct gre_minimal_pkt {
struct ethhdr eth;
struct iphdr iph;
struct gre_base_hdr gre_hdr;
struct iphdr iph_inner;
struct tcphdr tcp;
} __packed;
struct test {
const char *name;
union {
@ -98,6 +86,7 @@ struct test {
struct ipv6_pkt ipv6;
struct ipv6_frag_pkt ipv6_frag;
struct dvlan_ipv6_pkt dvlan_ipv6;
struct gre_minimal_pkt gre_minimal;
} pkt;
struct bpf_flow_keys keys;
__u32 flags;
@ -106,7 +95,6 @@ struct test {
#define VLAN_HLEN 4
static __u32 duration;
struct test tests[] = {
{
.name = "ipv4",
@ -444,8 +432,137 @@ struct test tests[] = {
},
.retval = BPF_FLOW_DISSECTOR_CONTINUE,
},
{
.name = "ip-gre",
.pkt.gre_minimal = {
.eth.h_proto = __bpf_constant_htons(ETH_P_IP),
.iph.ihl = 5,
.iph.protocol = IPPROTO_GRE,
.iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
.gre_hdr = {
.flags = 0,
.protocol = __bpf_constant_htons(ETH_P_IP),
},
.iph_inner.ihl = 5,
.iph_inner.protocol = IPPROTO_TCP,
.iph_inner.tot_len =
__bpf_constant_htons(MAGIC_BYTES -
sizeof(struct iphdr)),
.tcp.doff = 5,
.tcp.source = 80,
.tcp.dest = 8080,
},
.keys = {
.nhoff = ETH_HLEN,
.thoff = ETH_HLEN + sizeof(struct iphdr) * 2 +
sizeof(struct gre_base_hdr),
.addr_proto = ETH_P_IP,
.ip_proto = IPPROTO_TCP,
.n_proto = __bpf_constant_htons(ETH_P_IP),
.is_encap = true,
.sport = 80,
.dport = 8080,
},
.retval = BPF_OK,
},
{
.name = "ip-gre-no-encap",
.pkt.ipip = {
.eth.h_proto = __bpf_constant_htons(ETH_P_IP),
.iph.ihl = 5,
.iph.protocol = IPPROTO_GRE,
.iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
.iph_inner.ihl = 5,
.iph_inner.protocol = IPPROTO_TCP,
.iph_inner.tot_len =
__bpf_constant_htons(MAGIC_BYTES -
sizeof(struct iphdr)),
.tcp.doff = 5,
.tcp.source = 80,
.tcp.dest = 8080,
},
.keys = {
.flags = BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP,
.nhoff = ETH_HLEN,
.thoff = ETH_HLEN + sizeof(struct iphdr)
+ sizeof(struct gre_base_hdr),
.addr_proto = ETH_P_IP,
.ip_proto = IPPROTO_GRE,
.n_proto = __bpf_constant_htons(ETH_P_IP),
.is_encap = true,
},
.flags = BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP,
.retval = BPF_OK,
},
};
void serial_test_flow_dissector_namespace(void)
{
struct bpf_flow *skel;
struct nstoken *ns;
int err, prog_fd;
skel = bpf_flow__open_and_load();
if (!ASSERT_OK_PTR(skel, "open/load skeleton"))
return;
prog_fd = bpf_program__fd(skel->progs._dissect);
if (!ASSERT_OK_FD(prog_fd, "get dissector fd"))
goto out_destroy_skel;
/* We must be able to attach a flow dissector to root namespace */
err = bpf_prog_attach(prog_fd, 0, BPF_FLOW_DISSECTOR, 0);
if (!ASSERT_OK(err, "attach on root namespace ok"))
goto out_destroy_skel;
err = make_netns(TEST_NS);
if (!ASSERT_OK(err, "create non-root net namespace"))
goto out_destroy_skel;
/* We must not be able to additionally attach a flow dissector to a
* non-root net namespace
*/
ns = open_netns(TEST_NS);
if (!ASSERT_OK_PTR(ns, "enter non-root net namespace"))
goto out_clean_ns;
err = bpf_prog_attach(prog_fd, 0, BPF_FLOW_DISSECTOR, 0);
if (!ASSERT_ERR(err,
"refuse new flow dissector in non-root net namespace"))
bpf_prog_detach2(prog_fd, 0, BPF_FLOW_DISSECTOR);
else
ASSERT_EQ(errno, EEXIST,
"refused because of already attached prog");
close_netns(ns);
/* If no flow dissector is attached to the root namespace, we must
* be able to attach one to a non-root net namespace
*/
bpf_prog_detach2(prog_fd, 0, BPF_FLOW_DISSECTOR);
ns = open_netns(TEST_NS);
ASSERT_OK_PTR(ns, "enter non-root net namespace");
err = bpf_prog_attach(prog_fd, 0, BPF_FLOW_DISSECTOR, 0);
close_netns(ns);
ASSERT_OK(err, "accept new flow dissector in non-root net namespace");
/* If a flow dissector is attached to non-root net namespace, attaching
* a flow dissector to root namespace must fail
*/
err = bpf_prog_attach(prog_fd, 0, BPF_FLOW_DISSECTOR, 0);
if (!ASSERT_ERR(err, "refuse new flow dissector on root namespace"))
bpf_prog_detach2(prog_fd, 0, BPF_FLOW_DISSECTOR);
else
ASSERT_EQ(errno, EEXIST,
"refused because of already attached prog");
ns = open_netns(TEST_NS);
bpf_prog_detach2(prog_fd, 0, BPF_FLOW_DISSECTOR);
close_netns(ns);
out_clean_ns:
remove_netns(TEST_NS);
out_destroy_skel:
bpf_flow__destroy(skel);
}
static int create_tap(const char *ifname)
{
struct ifreq ifr = {
@ -533,22 +650,27 @@ static int init_prog_array(struct bpf_object *obj, struct bpf_map *prog_array)
return 0;
}
static void run_tests_skb_less(int tap_fd, struct bpf_map *keys)
static void run_tests_skb_less(int tap_fd, struct bpf_map *keys,
char *test_suffix)
{
char test_name[TEST_NAME_MAX_LEN];
int i, err, keys_fd;
keys_fd = bpf_map__fd(keys);
if (CHECK(keys_fd < 0, "bpf_map__fd", "err %d\n", keys_fd))
if (!ASSERT_OK_FD(keys_fd, "bpf_map__fd"))
return;
for (i = 0; i < ARRAY_SIZE(tests); i++) {
/* Keep in sync with 'flags' from eth_get_headlen. */
__u32 eth_get_headlen_flags =
BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG;
LIBBPF_OPTS(bpf_test_run_opts, topts);
struct bpf_flow_keys flow_keys = {};
__u32 key = (__u32)(tests[i].keys.sport) << 16 |
tests[i].keys.dport;
snprintf(test_name, TEST_NAME_MAX_LEN, "%s-%s", tests[i].name,
test_suffix);
if (!test__start_subtest(test_name))
continue;
/* For skb-less case we can't pass input flags; run
* only the tests that have a matching set of flags.
@ -558,78 +680,139 @@ static void run_tests_skb_less(int tap_fd, struct bpf_map *keys)
continue;
err = tx_tap(tap_fd, &tests[i].pkt, sizeof(tests[i].pkt));
CHECK(err < 0, "tx_tap", "err %d errno %d\n", err, errno);
if (!ASSERT_EQ(err, sizeof(tests[i].pkt), "tx_tap"))
continue;
/* check the stored flow_keys only if BPF_OK expected */
if (tests[i].retval != BPF_OK)
continue;
err = bpf_map_lookup_elem(keys_fd, &key, &flow_keys);
ASSERT_OK(err, "bpf_map_lookup_elem");
if (!ASSERT_OK(err, "bpf_map_lookup_elem"))
continue;
CHECK_FLOW_KEYS(tests[i].name, flow_keys, tests[i].keys);
ASSERT_MEMEQ(&flow_keys, &tests[i].keys,
sizeof(struct bpf_flow_keys),
"returned flow keys");
err = bpf_map_delete_elem(keys_fd, &key);
ASSERT_OK(err, "bpf_map_delete_elem");
}
}
static void test_skb_less_prog_attach(struct bpf_flow *skel, int tap_fd)
void test_flow_dissector_skb_less_direct_attach(void)
{
int err, prog_fd;
int err, prog_fd, tap_fd;
struct bpf_flow *skel;
struct netns_obj *ns;
ns = netns_new("flow_dissector_skb_less_indirect_attach_ns", true);
if (!ASSERT_OK_PTR(ns, "create and open netns"))
return;
skel = bpf_flow__open_and_load();
if (!ASSERT_OK_PTR(skel, "open/load skeleton"))
goto out_clean_ns;
err = init_prog_array(skel->obj, skel->maps.jmp_table);
if (!ASSERT_OK(err, "init_prog_array"))
goto out_destroy_skel;
prog_fd = bpf_program__fd(skel->progs._dissect);
if (CHECK(prog_fd < 0, "bpf_program__fd", "err %d\n", prog_fd))
return;
if (!ASSERT_OK_FD(prog_fd, "bpf_program__fd"))
goto out_destroy_skel;
err = bpf_prog_attach(prog_fd, 0, BPF_FLOW_DISSECTOR, 0);
if (CHECK(err, "bpf_prog_attach", "err %d errno %d\n", err, errno))
return;
if (!ASSERT_OK(err, "bpf_prog_attach"))
goto out_destroy_skel;
run_tests_skb_less(tap_fd, skel->maps.last_dissection);
tap_fd = create_tap("tap0");
if (!ASSERT_OK_FD(tap_fd, "create_tap"))
goto out_destroy_skel;
err = ifup("tap0");
if (!ASSERT_OK(err, "ifup"))
goto out_close_tap;
run_tests_skb_less(tap_fd, skel->maps.last_dissection,
"non-skb-direct-attach");
err = bpf_prog_detach2(prog_fd, 0, BPF_FLOW_DISSECTOR);
CHECK(err, "bpf_prog_detach2", "err %d errno %d\n", err, errno);
ASSERT_OK(err, "bpf_prog_detach2");
out_close_tap:
close(tap_fd);
out_destroy_skel:
bpf_flow__destroy(skel);
out_clean_ns:
netns_free(ns);
}
static void test_skb_less_link_create(struct bpf_flow *skel, int tap_fd)
void test_flow_dissector_skb_less_indirect_attach(void)
{
int err, net_fd, tap_fd;
struct bpf_flow *skel;
struct bpf_link *link;
int err, net_fd;
struct netns_obj *ns;
ns = netns_new("flow_dissector_skb_less_indirect_attach_ns", true);
if (!ASSERT_OK_PTR(ns, "create and open netns"))
return;
skel = bpf_flow__open_and_load();
if (!ASSERT_OK_PTR(skel, "open/load skeleton"))
goto out_clean_ns;
net_fd = open("/proc/self/ns/net", O_RDONLY);
if (CHECK(net_fd < 0, "open(/proc/self/ns/net)", "err %d\n", errno))
return;
if (!ASSERT_OK_FD(net_fd, "open(/proc/self/ns/net"))
goto out_destroy_skel;
err = init_prog_array(skel->obj, skel->maps.jmp_table);
if (!ASSERT_OK(err, "init_prog_array"))
goto out_destroy_skel;
tap_fd = create_tap("tap0");
if (!ASSERT_OK_FD(tap_fd, "create_tap"))
goto out_close_ns;
err = ifup("tap0");
if (!ASSERT_OK(err, "ifup"))
goto out_close_tap;
link = bpf_program__attach_netns(skel->progs._dissect, net_fd);
if (!ASSERT_OK_PTR(link, "attach_netns"))
goto out_close;
goto out_close_tap;
run_tests_skb_less(tap_fd, skel->maps.last_dissection);
run_tests_skb_less(tap_fd, skel->maps.last_dissection,
"non-skb-indirect-attach");
err = bpf_link__destroy(link);
CHECK(err, "bpf_link__destroy", "err %d\n", err);
out_close:
ASSERT_OK(err, "bpf_link__destroy");
out_close_tap:
close(tap_fd);
out_close_ns:
close(net_fd);
out_destroy_skel:
bpf_flow__destroy(skel);
out_clean_ns:
netns_free(ns);
}
void test_flow_dissector(void)
void test_flow_dissector_skb(void)
{
int i, err, prog_fd, keys_fd = -1, tap_fd;
char test_name[TEST_NAME_MAX_LEN];
struct bpf_flow *skel;
int i, err, prog_fd;
skel = bpf_flow__open_and_load();
if (CHECK(!skel, "skel", "failed to open/load skeleton\n"))
if (!ASSERT_OK_PTR(skel, "open/load skeleton"))
return;
prog_fd = bpf_program__fd(skel->progs._dissect);
if (CHECK(prog_fd < 0, "bpf_program__fd", "err %d\n", prog_fd))
goto out_destroy_skel;
keys_fd = bpf_map__fd(skel->maps.last_dissection);
if (CHECK(keys_fd < 0, "bpf_map__fd", "err %d\n", keys_fd))
goto out_destroy_skel;
err = init_prog_array(skel->obj, skel->maps.jmp_table);
if (CHECK(err, "init_prog_array", "err %d\n", err))
if (!ASSERT_OK(err, "init_prog_array"))
goto out_destroy_skel;
prog_fd = bpf_program__fd(skel->progs._dissect);
if (!ASSERT_OK_FD(prog_fd, "bpf_program__fd"))
goto out_destroy_skel;
for (i = 0; i < ARRAY_SIZE(tests); i++) {
@ -641,6 +824,10 @@ void test_flow_dissector(void)
);
static struct bpf_flow_keys ctx = {};
snprintf(test_name, TEST_NAME_MAX_LEN, "%s-skb", tests[i].name);
if (!test__start_subtest(test_name))
continue;
if (tests[i].flags) {
topts.ctx_in = &ctx;
topts.ctx_size_in = sizeof(ctx);
@ -656,26 +843,12 @@ void test_flow_dissector(void)
continue;
ASSERT_EQ(topts.data_size_out, sizeof(flow_keys),
"test_run data_size_out");
CHECK_FLOW_KEYS(tests[i].name, flow_keys, tests[i].keys);
ASSERT_MEMEQ(&flow_keys, &tests[i].keys,
sizeof(struct bpf_flow_keys),
"returned flow keys");
}
/* Do the same tests but for skb-less flow dissector.
* We use a known path in the net/tun driver that calls
* eth_get_headlen and we manually export bpf_flow_keys
* via BPF map in this case.
*/
tap_fd = create_tap("tap0");
CHECK(tap_fd < 0, "create_tap", "tap_fd %d errno %d\n", tap_fd, errno);
err = ifup("tap0");
CHECK(err, "ifup", "err %d errno %d\n", err, errno);
/* Test direct prog attachment */
test_skb_less_prog_attach(skel, tap_fd);
/* Test indirect prog attachment via link */
test_skb_less_link_create(skel, tap_fd);
close(tap_fd);
out_destroy_skel:
bpf_flow__destroy(skel);
}

View File

@ -0,0 +1,792 @@
// SPDX-License-Identifier: GPL-2.0
#define _GNU_SOURCE
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <bpf/bpf.h>
#include <linux/bpf.h>
#include <bpf/libbpf.h>
#include <arpa/inet.h>
#include <asm/byteorder.h>
#include <netinet/udp.h>
#include <poll.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <unistd.h>
#include "test_progs.h"
#include "network_helpers.h"
#include "bpf_util.h"
#include "bpf_flow.skel.h"
#define CFG_PORT_INNER 8000
#define CFG_PORT_GUE 6080
#define SUBTEST_NAME_MAX_LEN 32
#define TEST_NAME_MAX_LEN (32 + SUBTEST_NAME_MAX_LEN)
#define MAX_SOURCE_PORTS 3
#define TEST_PACKETS_COUNT 10
#define TEST_PACKET_LEN 100
#define TEST_PACKET_PATTERN 'a'
#define TEST_IPV4 "192.168.0.1/32"
#define TEST_IPV6 "100::a/128"
#define TEST_TUNNEL_REMOTE "127.0.0.2"
#define TEST_TUNNEL_LOCAL "127.0.0.1"
#define INIT_ADDR4(addr4, port) \
{ \
.sin_family = AF_INET, \
.sin_port = __constant_htons(port), \
.sin_addr.s_addr = __constant_htonl(addr4), \
}
#define INIT_ADDR6(addr6, port) \
{ \
.sin6_family = AF_INET6, \
.sin6_port = __constant_htons(port), \
.sin6_addr = addr6, \
}
#define TEST_IN4_SRC_ADDR_DEFAULT INIT_ADDR4(INADDR_LOOPBACK + 2, 0)
#define TEST_IN4_DST_ADDR_DEFAULT INIT_ADDR4(INADDR_LOOPBACK, CFG_PORT_INNER)
#define TEST_OUT4_SRC_ADDR_DEFAULT INIT_ADDR4(INADDR_LOOPBACK + 1, 0)
#define TEST_OUT4_DST_ADDR_DEFAULT INIT_ADDR4(INADDR_LOOPBACK, 0)
#define TEST_IN6_SRC_ADDR_DEFAULT INIT_ADDR6(IN6ADDR_LOOPBACK_INIT, 0)
#define TEST_IN6_DST_ADDR_DEFAULT \
INIT_ADDR6(IN6ADDR_LOOPBACK_INIT, CFG_PORT_INNER)
#define TEST_OUT6_SRC_ADDR_DEFAULT INIT_ADDR6(IN6ADDR_LOOPBACK_INIT, 0)
#define TEST_OUT6_DST_ADDR_DEFAULT INIT_ADDR6(IN6ADDR_LOOPBACK_INIT, 0)
#define TEST_IN4_SRC_ADDR_DISSECT_CONTINUE INIT_ADDR4(INADDR_LOOPBACK + 126, 0)
#define TEST_IN4_SRC_ADDR_IPIP INIT_ADDR4((in_addr_t)0x01010101, 0)
#define TEST_IN4_DST_ADDR_IPIP INIT_ADDR4((in_addr_t)0xC0A80001, CFG_PORT_INNER)
struct grehdr {
uint16_t unused;
uint16_t protocol;
} __packed;
struct guehdr {
union {
struct {
#if defined(__LITTLE_ENDIAN_BITFIELD)
__u8 hlen : 5, control : 1, version : 2;
#elif defined(__BIG_ENDIAN_BITFIELD)
__u8 version : 2, control : 1, hlen : 5;
#else
#error "Please fix <asm/byteorder.h>"
#endif
__u8 proto_ctype;
__be16 flags;
};
__be32 word;
};
};
static char buf[ETH_DATA_LEN];
struct test_configuration {
char name[SUBTEST_NAME_MAX_LEN];
int (*test_setup)(void);
void (*test_teardown)(void);
int source_ports[MAX_SOURCE_PORTS];
int cfg_l3_inner;
struct sockaddr_in in_saddr4;
struct sockaddr_in in_daddr4;
struct sockaddr_in6 in_saddr6;
struct sockaddr_in6 in_daddr6;
int cfg_l3_outer;
struct sockaddr_in out_saddr4;
struct sockaddr_in out_daddr4;
struct sockaddr_in6 out_saddr6;
struct sockaddr_in6 out_daddr6;
int cfg_encap_proto;
uint8_t cfg_dsfield_inner;
uint8_t cfg_dsfield_outer;
int cfg_l3_extra;
struct sockaddr_in extra_saddr4;
struct sockaddr_in extra_daddr4;
struct sockaddr_in6 extra_saddr6;
struct sockaddr_in6 extra_daddr6;
};
static unsigned long util_gettime(void)
{
struct timeval tv;
gettimeofday(&tv, NULL);
return (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
}
static void build_ipv4_header(void *header, uint8_t proto, uint32_t src,
uint32_t dst, int payload_len, uint8_t tos)
{
struct iphdr *iph = header;
iph->ihl = 5;
iph->version = 4;
iph->tos = tos;
iph->ttl = 8;
iph->tot_len = htons(sizeof(*iph) + payload_len);
iph->id = htons(1337);
iph->protocol = proto;
iph->saddr = src;
iph->daddr = dst;
iph->check = build_ip_csum((void *)iph);
}
static void ipv6_set_dsfield(struct ipv6hdr *ip6h, uint8_t dsfield)
{
uint16_t val, *ptr = (uint16_t *)ip6h;
val = ntohs(*ptr);
val &= 0xF00F;
val |= ((uint16_t)dsfield) << 4;
*ptr = htons(val);
}
static void build_ipv6_header(void *header, uint8_t proto,
const struct sockaddr_in6 *src,
const struct sockaddr_in6 *dst, int payload_len,
uint8_t dsfield)
{
struct ipv6hdr *ip6h = header;
ip6h->version = 6;
ip6h->payload_len = htons(payload_len);
ip6h->nexthdr = proto;
ip6h->hop_limit = 8;
ipv6_set_dsfield(ip6h, dsfield);
memcpy(&ip6h->saddr, &src->sin6_addr, sizeof(ip6h->saddr));
memcpy(&ip6h->daddr, &dst->sin6_addr, sizeof(ip6h->daddr));
}
static void build_udp_header(void *header, int payload_len, uint16_t sport,
uint16_t dport, int family)
{
struct udphdr *udph = header;
int len = sizeof(*udph) + payload_len;
udph->source = htons(sport);
udph->dest = htons(dport);
udph->len = htons(len);
udph->check = 0;
if (family == AF_INET)
udph->check = build_udp_v4_csum(header - sizeof(struct iphdr),
udph);
else
udph->check = build_udp_v6_csum(header - sizeof(struct ipv6hdr),
udph);
}
static void build_gue_header(void *header, uint8_t proto)
{
struct guehdr *gueh = header;
gueh->proto_ctype = proto;
}
static void build_gre_header(void *header, uint16_t proto)
{
struct grehdr *greh = header;
greh->protocol = htons(proto);
}
static int l3_length(int family)
{
if (family == AF_INET)
return sizeof(struct iphdr);
else
return sizeof(struct ipv6hdr);
}
static int build_packet(const struct test_configuration *test, uint16_t sport)
{
int ol3_len = 0, ol4_len = 0, il3_len = 0, il4_len = 0;
int el3_len = 0, packet_len;
memset(buf, 0, ETH_DATA_LEN);
if (test->cfg_l3_extra)
el3_len = l3_length(test->cfg_l3_extra);
/* calculate header offsets */
if (test->cfg_encap_proto) {
ol3_len = l3_length(test->cfg_l3_outer);
if (test->cfg_encap_proto == IPPROTO_GRE)
ol4_len = sizeof(struct grehdr);
else if (test->cfg_encap_proto == IPPROTO_UDP)
ol4_len = sizeof(struct udphdr) + sizeof(struct guehdr);
}
il3_len = l3_length(test->cfg_l3_inner);
il4_len = sizeof(struct udphdr);
packet_len = el3_len + ol3_len + ol4_len + il3_len + il4_len +
TEST_PACKET_LEN;
if (!ASSERT_LE(packet_len, sizeof(buf), "check packet size"))
return -1;
/*
* Fill packet from inside out, to calculate correct checksums.
* But create ip before udp headers, as udp uses ip for pseudo-sum.
*/
memset(buf + el3_len + ol3_len + ol4_len + il3_len + il4_len,
TEST_PACKET_PATTERN, TEST_PACKET_LEN);
/* add zero byte for udp csum padding */
buf[el3_len + ol3_len + ol4_len + il3_len + il4_len + TEST_PACKET_LEN] =
0;
switch (test->cfg_l3_inner) {
case PF_INET:
build_ipv4_header(buf + el3_len + ol3_len + ol4_len,
IPPROTO_UDP, test->in_saddr4.sin_addr.s_addr,
test->in_daddr4.sin_addr.s_addr,
il4_len + TEST_PACKET_LEN,
test->cfg_dsfield_inner);
break;
case PF_INET6:
build_ipv6_header(buf + el3_len + ol3_len + ol4_len,
IPPROTO_UDP, &test->in_saddr6,
&test->in_daddr6, il4_len + TEST_PACKET_LEN,
test->cfg_dsfield_inner);
break;
}
build_udp_header(buf + el3_len + ol3_len + ol4_len + il3_len,
TEST_PACKET_LEN, sport, CFG_PORT_INNER,
test->cfg_l3_inner);
if (!test->cfg_encap_proto)
return il3_len + il4_len + TEST_PACKET_LEN;
switch (test->cfg_l3_outer) {
case PF_INET:
build_ipv4_header(buf + el3_len, test->cfg_encap_proto,
test->out_saddr4.sin_addr.s_addr,
test->out_daddr4.sin_addr.s_addr,
ol4_len + il3_len + il4_len + TEST_PACKET_LEN,
test->cfg_dsfield_outer);
break;
case PF_INET6:
build_ipv6_header(buf + el3_len, test->cfg_encap_proto,
&test->out_saddr6, &test->out_daddr6,
ol4_len + il3_len + il4_len + TEST_PACKET_LEN,
test->cfg_dsfield_outer);
break;
}
switch (test->cfg_encap_proto) {
case IPPROTO_UDP:
build_gue_header(buf + el3_len + ol3_len + ol4_len -
sizeof(struct guehdr),
test->cfg_l3_inner == PF_INET ? IPPROTO_IPIP :
IPPROTO_IPV6);
build_udp_header(buf + el3_len + ol3_len,
sizeof(struct guehdr) + il3_len + il4_len +
TEST_PACKET_LEN,
sport, CFG_PORT_GUE, test->cfg_l3_outer);
break;
case IPPROTO_GRE:
build_gre_header(buf + el3_len + ol3_len,
test->cfg_l3_inner == PF_INET ? ETH_P_IP :
ETH_P_IPV6);
break;
}
switch (test->cfg_l3_extra) {
case PF_INET:
build_ipv4_header(buf,
test->cfg_l3_outer == PF_INET ? IPPROTO_IPIP :
IPPROTO_IPV6,
test->extra_saddr4.sin_addr.s_addr,
test->extra_daddr4.sin_addr.s_addr,
ol3_len + ol4_len + il3_len + il4_len +
TEST_PACKET_LEN,
0);
break;
case PF_INET6:
build_ipv6_header(buf,
test->cfg_l3_outer == PF_INET ? IPPROTO_IPIP :
IPPROTO_IPV6,
&test->extra_saddr6, &test->extra_daddr6,
ol3_len + ol4_len + il3_len + il4_len +
TEST_PACKET_LEN,
0);
break;
}
return el3_len + ol3_len + ol4_len + il3_len + il4_len +
TEST_PACKET_LEN;
}
/* sender transmits encapsulated over RAW or unencap'd over UDP */
static int setup_tx(const struct test_configuration *test)
{
int family, fd, ret;
if (test->cfg_l3_extra)
family = test->cfg_l3_extra;
else if (test->cfg_l3_outer)
family = test->cfg_l3_outer;
else
family = test->cfg_l3_inner;
fd = socket(family, SOCK_RAW, IPPROTO_RAW);
if (!ASSERT_OK_FD(fd, "setup tx socket"))
return fd;
if (test->cfg_l3_extra) {
if (test->cfg_l3_extra == PF_INET)
ret = connect(fd, (void *)&test->extra_daddr4,
sizeof(test->extra_daddr4));
else
ret = connect(fd, (void *)&test->extra_daddr6,
sizeof(test->extra_daddr6));
if (!ASSERT_OK(ret, "connect")) {
close(fd);
return ret;
}
} else if (test->cfg_l3_outer) {
/* connect to destination if not encapsulated */
if (test->cfg_l3_outer == PF_INET)
ret = connect(fd, (void *)&test->out_daddr4,
sizeof(test->out_daddr4));
else
ret = connect(fd, (void *)&test->out_daddr6,
sizeof(test->out_daddr6));
if (!ASSERT_OK(ret, "connect")) {
close(fd);
return ret;
}
} else {
/* otherwise using loopback */
if (test->cfg_l3_inner == PF_INET)
ret = connect(fd, (void *)&test->in_daddr4,
sizeof(test->in_daddr4));
else
ret = connect(fd, (void *)&test->in_daddr6,
sizeof(test->in_daddr6));
if (!ASSERT_OK(ret, "connect")) {
close(fd);
return ret;
}
}
return fd;
}
/* receiver reads unencapsulated UDP */
static int setup_rx(const struct test_configuration *test)
{
int fd, ret;
fd = socket(test->cfg_l3_inner, SOCK_DGRAM, 0);
if (!ASSERT_OK_FD(fd, "socket rx"))
return fd;
if (test->cfg_l3_inner == PF_INET)
ret = bind(fd, (void *)&test->in_daddr4,
sizeof(test->in_daddr4));
else
ret = bind(fd, (void *)&test->in_daddr6,
sizeof(test->in_daddr6));
if (!ASSERT_OK(ret, "bind rx")) {
close(fd);
return ret;
}
return fd;
}
static int do_tx(int fd, const char *pkt, int len)
{
int ret;
ret = write(fd, pkt, len);
return ret != len;
}
static int do_poll(int fd, short events, int timeout)
{
struct pollfd pfd;
int ret;
pfd.fd = fd;
pfd.events = events;
ret = poll(&pfd, 1, timeout);
return ret;
}
static int do_rx(int fd)
{
char rbuf;
int ret, num = 0;
while (1) {
ret = recv(fd, &rbuf, 1, MSG_DONTWAIT);
if (ret == -1 && errno == EAGAIN)
break;
if (ret < 0)
return -1;
if (!ASSERT_EQ(rbuf, TEST_PACKET_PATTERN, "check pkt pattern"))
return -1;
num++;
}
return num;
}
static int run_test(const struct test_configuration *test,
int source_port_index)
{
int fdt = -1, fdr = -1, len, tx = 0, rx = 0, err;
unsigned long tstop, tcur;
fdr = setup_rx(test);
fdt = setup_tx(test);
if (!ASSERT_OK_FD(fdr, "setup rx") || !ASSERT_OK_FD(fdt, "setup tx")) {
err = -1;
goto out_close_sockets;
}
len = build_packet(test,
(uint16_t)test->source_ports[source_port_index]);
if (!ASSERT_GT(len, 0, "build test packet"))
return -1;
tcur = util_gettime();
tstop = tcur;
while (tx < TEST_PACKETS_COUNT) {
if (!ASSERT_OK(do_tx(fdt, buf, len), "do_tx"))
break;
tx++;
err = do_rx(fdr);
if (!ASSERT_GE(err, 0, "do_rx"))
break;
rx += err;
}
/* read straggler packets, if any */
if (rx < tx) {
tstop = util_gettime() + 100;
while (rx < tx) {
tcur = util_gettime();
if (tcur >= tstop)
break;
err = do_poll(fdr, POLLIN, tstop - tcur);
if (err < 0)
break;
err = do_rx(fdr);
if (err >= 0)
rx += err;
}
}
out_close_sockets:
close(fdt);
close(fdr);
return rx;
}
static int attach_and_configure_program(struct bpf_flow *skel)
{
struct bpf_map *prog_array = skel->maps.jmp_table;
int main_prog_fd, sub_prog_fd, map_fd, i, err;
struct bpf_program *prog;
char prog_name[32];
main_prog_fd = bpf_program__fd(skel->progs._dissect);
if (main_prog_fd < 0)
return main_prog_fd;
err = bpf_prog_attach(main_prog_fd, 0, BPF_FLOW_DISSECTOR, 0);
if (err)
return err;
map_fd = bpf_map__fd(prog_array);
if (map_fd < 0)
return map_fd;
for (i = 0; i < bpf_map__max_entries(prog_array); i++) {
snprintf(prog_name, sizeof(prog_name), "flow_dissector_%d", i);
prog = bpf_object__find_program_by_name(skel->obj, prog_name);
if (!prog)
return -1;
sub_prog_fd = bpf_program__fd(prog);
if (sub_prog_fd < 0)
return -1;
err = bpf_map_update_elem(map_fd, &i, &sub_prog_fd, BPF_ANY);
if (err)
return -1;
}
return main_prog_fd;
}
static void detach_program(struct bpf_flow *skel, int prog_fd)
{
bpf_prog_detach2(prog_fd, 0, BPF_FLOW_DISSECTOR);
}
static int set_port_drop(int pf, bool multi_port)
{
SYS(fail, "tc qdisc add dev lo ingress");
SYS(fail_delete_qdisc, "tc filter add %s %s %s %s %s %s %s %s %s %s",
"dev lo",
"parent FFFF:",
"protocol", pf == PF_INET6 ? "ipv6" : "ip",
"pref 1337",
"flower",
"ip_proto udp",
"src_port", multi_port ? "8-10" : "9",
"action drop");
return 0;
fail_delete_qdisc:
SYS_NOFAIL("tc qdisc del dev lo ingress");
fail:
return 1;
}
static void remove_filter(void)
{
SYS_NOFAIL("tc filter del dev lo ingress");
SYS_NOFAIL("tc qdisc del dev lo ingress");
}
static int ipv4_setup(void)
{
return set_port_drop(PF_INET, false);
}
static int ipv6_setup(void)
{
return set_port_drop(PF_INET6, false);
}
static int port_range_setup(void)
{
return set_port_drop(PF_INET, true);
}
static int set_addresses(void)
{
SYS(out, "ip -4 addr add %s dev lo", TEST_IPV4);
SYS(out_remove_ipv4, "ip -6 addr add %s dev lo", TEST_IPV6);
return 0;
out_remove_ipv4:
SYS_NOFAIL("ip -4 addr del %s dev lo", TEST_IPV4);
out:
return -1;
}
static void unset_addresses(void)
{
SYS_NOFAIL("ip -4 addr del %s dev lo", TEST_IPV4);
SYS_NOFAIL("ip -6 addr del %s dev lo", TEST_IPV6);
}
static int ipip_setup(void)
{
if (!ASSERT_OK(set_addresses(), "configure addresses"))
return -1;
if (!ASSERT_OK(set_port_drop(PF_INET, false), "set filter"))
goto out_unset_addresses;
SYS(out_remove_filter,
"ip link add ipip_test type ipip remote %s local %s dev lo",
TEST_TUNNEL_REMOTE, TEST_TUNNEL_LOCAL);
SYS(out_clean_netif, "ip link set ipip_test up");
return 0;
out_clean_netif:
SYS_NOFAIL("ip link del ipip_test");
out_remove_filter:
remove_filter();
out_unset_addresses:
unset_addresses();
return -1;
}
static void ipip_shutdown(void)
{
SYS_NOFAIL("ip link del ipip_test");
remove_filter();
unset_addresses();
}
static int gre_setup(void)
{
if (!ASSERT_OK(set_addresses(), "configure addresses"))
return -1;
if (!ASSERT_OK(set_port_drop(PF_INET, false), "set filter"))
goto out_unset_addresses;
SYS(out_remove_filter,
"ip link add gre_test type gre remote %s local %s dev lo",
TEST_TUNNEL_REMOTE, TEST_TUNNEL_LOCAL);
SYS(out_clean_netif, "ip link set gre_test up");
return 0;
out_clean_netif:
SYS_NOFAIL("ip link del ipip_test");
out_remove_filter:
remove_filter();
out_unset_addresses:
unset_addresses();
return -1;
}
static void gre_shutdown(void)
{
SYS_NOFAIL("ip link del gre_test");
remove_filter();
unset_addresses();
}
static const struct test_configuration tests_input[] = {
{
.name = "ipv4",
.test_setup = ipv4_setup,
.test_teardown = remove_filter,
.source_ports = { 8, 9, 10 },
.cfg_l3_inner = PF_INET,
.in_saddr4 = TEST_IN4_SRC_ADDR_DEFAULT,
.in_daddr4 = TEST_IN4_DST_ADDR_DEFAULT
},
{
.name = "ipv4_continue_dissect",
.test_setup = ipv4_setup,
.test_teardown = remove_filter,
.source_ports = { 8, 9, 10 },
.cfg_l3_inner = PF_INET,
.in_saddr4 = TEST_IN4_SRC_ADDR_DISSECT_CONTINUE,
.in_daddr4 = TEST_IN4_DST_ADDR_DEFAULT },
{
.name = "ipip",
.test_setup = ipip_setup,
.test_teardown = ipip_shutdown,
.source_ports = { 8, 9, 10 },
.cfg_l3_inner = PF_INET,
.in_saddr4 = TEST_IN4_SRC_ADDR_IPIP,
.in_daddr4 = TEST_IN4_DST_ADDR_IPIP,
.out_saddr4 = TEST_OUT4_SRC_ADDR_DEFAULT,
.out_daddr4 = TEST_OUT4_DST_ADDR_DEFAULT,
.cfg_l3_outer = PF_INET,
.cfg_encap_proto = IPPROTO_IPIP,
},
{
.name = "gre",
.test_setup = gre_setup,
.test_teardown = gre_shutdown,
.source_ports = { 8, 9, 10 },
.cfg_l3_inner = PF_INET,
.in_saddr4 = TEST_IN4_SRC_ADDR_IPIP,
.in_daddr4 = TEST_IN4_DST_ADDR_IPIP,
.out_saddr4 = TEST_OUT4_SRC_ADDR_DEFAULT,
.out_daddr4 = TEST_OUT4_DST_ADDR_DEFAULT,
.cfg_l3_outer = PF_INET,
.cfg_encap_proto = IPPROTO_GRE,
},
{
.name = "port_range",
.test_setup = port_range_setup,
.test_teardown = remove_filter,
.source_ports = { 7, 9, 11 },
.cfg_l3_inner = PF_INET,
.in_saddr4 = TEST_IN4_SRC_ADDR_DEFAULT,
.in_daddr4 = TEST_IN4_DST_ADDR_DEFAULT },
{
.name = "ipv6",
.test_setup = ipv6_setup,
.test_teardown = remove_filter,
.source_ports = { 8, 9, 10 },
.cfg_l3_inner = PF_INET6,
.in_saddr6 = TEST_IN6_SRC_ADDR_DEFAULT,
.in_daddr6 = TEST_IN6_DST_ADDR_DEFAULT
},
};
struct test_ctx {
struct bpf_flow *skel;
struct netns_obj *ns;
int prog_fd;
};
static int test_global_init(struct test_ctx *ctx)
{
int err;
ctx->skel = bpf_flow__open_and_load();
if (!ASSERT_OK_PTR(ctx->skel, "open and load flow_dissector"))
return -1;
ctx->ns = netns_new("flow_dissector_classification", true);
if (!ASSERT_OK_PTR(ctx->ns, "switch ns"))
goto out_destroy_skel;
err = write_sysctl("/proc/sys/net/ipv4/conf/default/rp_filter", "0");
err |= write_sysctl("/proc/sys/net/ipv4/conf/all/rp_filter", "0");
err |= write_sysctl("/proc/sys/net/ipv4/conf/lo/rp_filter", "0");
if (!ASSERT_OK(err, "configure net tunables"))
goto out_clean_ns;
ctx->prog_fd = attach_and_configure_program(ctx->skel);
if (!ASSERT_OK_FD(ctx->prog_fd, "attach and configure program"))
goto out_clean_ns;
return 0;
out_clean_ns:
netns_free(ctx->ns);
out_destroy_skel:
bpf_flow__destroy(ctx->skel);
return -1;
}
static void test_global_shutdown(struct test_ctx *ctx)
{
detach_program(ctx->skel, ctx->prog_fd);
netns_free(ctx->ns);
bpf_flow__destroy(ctx->skel);
}
void test_flow_dissector_classification(void)
{
struct test_ctx ctx;
const struct test_configuration *test;
int i;
if (test_global_init(&ctx))
return;
for (i = 0; i < ARRAY_SIZE(tests_input); i++) {
if (!test__start_subtest(tests_input[i].name))
continue;
test = &tests_input[i];
/* All tests are expected to have one rx-ok port first,
* then a non-working rx port, and finally a rx-ok port
*/
if (test->test_setup &&
!ASSERT_OK(test->test_setup(), "init filter"))
continue;
ASSERT_EQ(run_test(test, 0), TEST_PACKETS_COUNT,
"test first port");
ASSERT_EQ(run_test(test, 1), 0, "test second port");
ASSERT_EQ(run_test(test, 2), TEST_PACKETS_COUNT,
"test third port");
if (test->test_teardown)
test->test_teardown();
}
test_global_shutdown(&ctx);
}

View File

@ -2,7 +2,7 @@
#include <test_progs.h>
#include "cgroup_helpers.h"
#include <linux/tcp.h>
#include <netinet/tcp.h>
#include <linux/netlink.h>
#include "sockopt_sk.skel.h"

View File

@ -98,6 +98,7 @@
#include "verifier_xdp_direct_packet_access.skel.h"
#include "verifier_bits_iter.skel.h"
#include "verifier_lsm.skel.h"
#include "irq.skel.h"
#define MAX_ENTRIES 11
@ -225,6 +226,7 @@ void test_verifier_xdp(void) { RUN(verifier_xdp); }
void test_verifier_xdp_direct_packet_access(void) { RUN(verifier_xdp_direct_packet_access); }
void test_verifier_bits_iter(void) { RUN(verifier_bits_iter); }
void test_verifier_lsm(void) { RUN(verifier_lsm); }
void test_irq(void) { RUN(irq); }
void test_verifier_mtu(void) { RUN(verifier_mtu); }
static int init_test_val_map(struct bpf_object *obj, char *map_name)

View File

@ -17,7 +17,7 @@
#include "network_helpers.h"
#include <linux/if_bonding.h>
#include <linux/limits.h>
#include <linux/udp.h>
#include <netinet/udp.h>
#include <uapi/linux/netdev.h>
#include "xdp_dummy.skel.h"

View File

@ -2,6 +2,14 @@
#include <test_progs.h>
#include <network_helpers.h>
#include "test_xdp_context_test_run.skel.h"
#include "test_xdp_meta.skel.h"
#define TX_ADDR "10.0.0.1"
#define RX_ADDR "10.0.0.2"
#define RX_NAME "veth0"
#define TX_NAME "veth1"
#define TX_NETNS "xdp_context_tx"
#define RX_NETNS "xdp_context_rx"
void test_xdp_context_error(int prog_fd, struct bpf_test_run_opts opts,
__u32 data_meta, __u32 data, __u32 data_end,
@ -103,3 +111,82 @@ void test_xdp_context_test_run(void)
test_xdp_context_test_run__destroy(skel);
}
void test_xdp_context_functional(void)
{
LIBBPF_OPTS(bpf_tc_hook, tc_hook, .attach_point = BPF_TC_INGRESS);
LIBBPF_OPTS(bpf_tc_opts, tc_opts, .handle = 1, .priority = 1);
struct netns_obj *rx_ns = NULL, *tx_ns = NULL;
struct bpf_program *tc_prog, *xdp_prog;
struct test_xdp_meta *skel = NULL;
struct nstoken *nstoken = NULL;
int rx_ifindex;
int ret;
tx_ns = netns_new(TX_NETNS, false);
if (!ASSERT_OK_PTR(tx_ns, "create tx_ns"))
return;
rx_ns = netns_new(RX_NETNS, false);
if (!ASSERT_OK_PTR(rx_ns, "create rx_ns"))
goto close;
SYS(close, "ip link add " RX_NAME " netns " RX_NETNS
" type veth peer name " TX_NAME " netns " TX_NETNS);
nstoken = open_netns(RX_NETNS);
if (!ASSERT_OK_PTR(nstoken, "setns rx_ns"))
goto close;
SYS(close, "ip addr add " RX_ADDR "/24 dev " RX_NAME);
SYS(close, "ip link set dev " RX_NAME " up");
skel = test_xdp_meta__open_and_load();
if (!ASSERT_OK_PTR(skel, "open and load skeleton"))
goto close;
rx_ifindex = if_nametoindex(RX_NAME);
if (!ASSERT_GE(rx_ifindex, 0, "if_nametoindex rx"))
goto close;
tc_hook.ifindex = rx_ifindex;
ret = bpf_tc_hook_create(&tc_hook);
if (!ASSERT_OK(ret, "bpf_tc_hook_create"))
goto close;
tc_prog = bpf_object__find_program_by_name(skel->obj, "ing_cls");
if (!ASSERT_OK_PTR(tc_prog, "open ing_cls prog"))
goto close;
tc_opts.prog_fd = bpf_program__fd(tc_prog);
ret = bpf_tc_attach(&tc_hook, &tc_opts);
if (!ASSERT_OK(ret, "bpf_tc_attach"))
goto close;
xdp_prog = bpf_object__find_program_by_name(skel->obj, "ing_xdp");
if (!ASSERT_OK_PTR(xdp_prog, "open ing_xdp prog"))
goto close;
ret = bpf_xdp_attach(rx_ifindex,
bpf_program__fd(xdp_prog),
0, NULL);
if (!ASSERT_GE(ret, 0, "bpf_xdp_attach"))
goto close;
close_netns(nstoken);
nstoken = open_netns(TX_NETNS);
if (!ASSERT_OK_PTR(nstoken, "setns tx_ns"))
goto close;
SYS(close, "ip addr add " TX_ADDR "/24 dev " TX_NAME);
SYS(close, "ip link set dev " TX_NAME " up");
ASSERT_OK(SYS_NOFAIL("ping -c 1 " RX_ADDR), "ping");
close:
close_netns(nstoken);
test_xdp_meta__destroy(skel);
netns_free(rx_ns);
netns_free(tx_ns);
}

View File

@ -7,7 +7,7 @@
#include <linux/if_link.h>
#include <linux/ipv6.h>
#include <linux/in6.h>
#include <linux/udp.h>
#include <netinet/udp.h>
#include <bpf/bpf_endian.h>
#include <uapi/linux/netdev.h>
#include "test_xdp_do_redirect.skel.h"

View File

@ -3,7 +3,7 @@
#include <network_helpers.h>
#include <bpf/btf.h>
#include <linux/if_link.h>
#include <linux/udp.h>
#include <netinet/udp.h>
#include <net/if.h>
#include <unistd.h>

View File

@ -10,7 +10,7 @@
#include <linux/errqueue.h>
#include <linux/if_link.h>
#include <linux/net_tstamp.h>
#include <linux/udp.h>
#include <netinet/udp.h>
#include <sys/mman.h>
#include <net/if.h>
#include <poll.h>
@ -133,23 +133,6 @@ static void close_xsk(struct xsk *xsk)
munmap(xsk->umem_area, UMEM_SIZE);
}
static void ip_csum(struct iphdr *iph)
{
__u32 sum = 0;
__u16 *p;
int i;
iph->check = 0;
p = (void *)iph;
for (i = 0; i < sizeof(*iph) / sizeof(*p); i++)
sum += p[i];
while (sum >> 16)
sum = (sum & 0xffff) + (sum >> 16);
iph->check = ~sum;
}
static int generate_packet(struct xsk *xsk, __u16 dst_port)
{
struct xsk_tx_metadata *meta;
@ -192,7 +175,7 @@ static int generate_packet(struct xsk *xsk, __u16 dst_port)
iph->protocol = IPPROTO_UDP;
ASSERT_EQ(inet_pton(FAMILY, TX_ADDR, &iph->saddr), 1, "inet_pton(TX_ADDR)");
ASSERT_EQ(inet_pton(FAMILY, RX_ADDR, &iph->daddr), 1, "inet_pton(RX_ADDR)");
ip_csum(iph);
iph->check = build_ip_csum(iph);
udph->source = htons(UDP_SOURCE_PORT);
udph->dest = htons(dst_port);

View File

@ -3,7 +3,7 @@
#include <vmlinux.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include "../bpf_testmod/bpf_testmod.h"
#include "../test_kmods/bpf_testmod.h"
char _license[] SEC("license") = "GPL";

View File

@ -2,7 +2,7 @@
#include <vmlinux.h>
#include <bpf/bpf_tracing.h>
#include <bpf/bpf_helpers.h>
#include "../bpf_testmod/bpf_testmod_kfunc.h"
#include "../test_kmods/bpf_testmod_kfunc.h"
struct map_value {
struct prog_test_ref_kfunc __kptr *ptr;

View File

@ -0,0 +1,15 @@
// SPDX-License-Identifier: GPL-2.0
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
__u32 data_end;
SEC("cgroup_skb/ingress")
int direct_packet_access(struct __sk_buff *skb)
{
data_end = skb->data_end;
return 1;
}
char _license[] SEC("license") = "GPL";

View File

@ -4,8 +4,8 @@
#include <vmlinux.h>
#include <bpf/bpf_tracing.h>
#include "bpf_misc.h"
#include "../bpf_testmod/bpf_testmod.h"
#include "../bpf_testmod/bpf_testmod_kfunc.h"
#include "../test_kmods/bpf_testmod.h"
#include "../test_kmods/bpf_testmod_kfunc.h"
char _license[] SEC("license") = "GPL";

View File

@ -4,8 +4,8 @@
#include <vmlinux.h>
#include <bpf/bpf_tracing.h>
#include "bpf_misc.h"
#include "../bpf_testmod/bpf_testmod.h"
#include "../bpf_testmod/bpf_testmod_kfunc.h"
#include "../test_kmods/bpf_testmod.h"
#include "../test_kmods/bpf_testmod_kfunc.h"
char _license[] SEC("license") = "GPL";

View File

@ -131,7 +131,7 @@ int reject_subprog_with_lock(void *ctx)
}
SEC("?tc")
__failure __msg("BPF_EXIT instruction cannot be used inside bpf_rcu_read_lock-ed region")
__failure __msg("BPF_EXIT instruction in main prog cannot be used inside bpf_rcu_read_lock-ed region")
int reject_with_rcu_read_lock(void *ctx)
{
bpf_rcu_read_lock();
@ -147,7 +147,7 @@ __noinline static int throwing_subprog(struct __sk_buff *ctx)
}
SEC("?tc")
__failure __msg("BPF_EXIT instruction cannot be used inside bpf_rcu_read_lock-ed region")
__failure __msg("BPF_EXIT instruction in main prog cannot be used inside bpf_rcu_read_lock-ed region")
int reject_subprog_with_rcu_read_lock(void *ctx)
{
bpf_rcu_read_lock();

View File

@ -0,0 +1,444 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2024 Meta Platforms, Inc. and affiliates. */
#include <vmlinux.h>
#include <bpf/bpf_helpers.h>
#include "bpf_misc.h"
#include "bpf_experimental.h"
unsigned long global_flags;
extern void bpf_local_irq_save(unsigned long *) __weak __ksym;
extern void bpf_local_irq_restore(unsigned long *) __weak __ksym;
extern int bpf_copy_from_user_str(void *dst, u32 dst__sz, const void *unsafe_ptr__ign, u64 flags) __weak __ksym;
SEC("?tc")
__failure __msg("arg#0 doesn't point to an irq flag on stack")
int irq_save_bad_arg(struct __sk_buff *ctx)
{
bpf_local_irq_save(&global_flags);
return 0;
}
SEC("?tc")
__failure __msg("arg#0 doesn't point to an irq flag on stack")
int irq_restore_bad_arg(struct __sk_buff *ctx)
{
bpf_local_irq_restore(&global_flags);
return 0;
}
SEC("?tc")
__failure __msg("BPF_EXIT instruction in main prog cannot be used inside bpf_local_irq_save-ed region")
int irq_restore_missing_2(struct __sk_buff *ctx)
{
unsigned long flags1;
unsigned long flags2;
bpf_local_irq_save(&flags1);
bpf_local_irq_save(&flags2);
return 0;
}
SEC("?tc")
__failure __msg("BPF_EXIT instruction in main prog cannot be used inside bpf_local_irq_save-ed region")
int irq_restore_missing_3(struct __sk_buff *ctx)
{
unsigned long flags1;
unsigned long flags2;
unsigned long flags3;
bpf_local_irq_save(&flags1);
bpf_local_irq_save(&flags2);
bpf_local_irq_save(&flags3);
return 0;
}
SEC("?tc")
__failure __msg("BPF_EXIT instruction in main prog cannot be used inside bpf_local_irq_save-ed region")
int irq_restore_missing_3_minus_2(struct __sk_buff *ctx)
{
unsigned long flags1;
unsigned long flags2;
unsigned long flags3;
bpf_local_irq_save(&flags1);
bpf_local_irq_save(&flags2);
bpf_local_irq_save(&flags3);
bpf_local_irq_restore(&flags3);
bpf_local_irq_restore(&flags2);
return 0;
}
static __noinline void local_irq_save(unsigned long *flags)
{
bpf_local_irq_save(flags);
}
static __noinline void local_irq_restore(unsigned long *flags)
{
bpf_local_irq_restore(flags);
}
SEC("?tc")
__failure __msg("BPF_EXIT instruction in main prog cannot be used inside bpf_local_irq_save-ed region")
int irq_restore_missing_1_subprog(struct __sk_buff *ctx)
{
unsigned long flags;
local_irq_save(&flags);
return 0;
}
SEC("?tc")
__failure __msg("BPF_EXIT instruction in main prog cannot be used inside bpf_local_irq_save-ed region")
int irq_restore_missing_2_subprog(struct __sk_buff *ctx)
{
unsigned long flags1;
unsigned long flags2;
local_irq_save(&flags1);
local_irq_save(&flags2);
return 0;
}
SEC("?tc")
__failure __msg("BPF_EXIT instruction in main prog cannot be used inside bpf_local_irq_save-ed region")
int irq_restore_missing_3_subprog(struct __sk_buff *ctx)
{
unsigned long flags1;
unsigned long flags2;
unsigned long flags3;
local_irq_save(&flags1);
local_irq_save(&flags2);
local_irq_save(&flags3);
return 0;
}
SEC("?tc")
__failure __msg("BPF_EXIT instruction in main prog cannot be used inside bpf_local_irq_save-ed region")
int irq_restore_missing_3_minus_2_subprog(struct __sk_buff *ctx)
{
unsigned long flags1;
unsigned long flags2;
unsigned long flags3;
local_irq_save(&flags1);
local_irq_save(&flags2);
local_irq_save(&flags3);
local_irq_restore(&flags3);
local_irq_restore(&flags2);
return 0;
}
SEC("?tc")
__success
int irq_balance(struct __sk_buff *ctx)
{
unsigned long flags;
local_irq_save(&flags);
local_irq_restore(&flags);
return 0;
}
SEC("?tc")
__success
int irq_balance_n(struct __sk_buff *ctx)
{
unsigned long flags1;
unsigned long flags2;
unsigned long flags3;
local_irq_save(&flags1);
local_irq_save(&flags2);
local_irq_save(&flags3);
local_irq_restore(&flags3);
local_irq_restore(&flags2);
local_irq_restore(&flags1);
return 0;
}
static __noinline void local_irq_balance(void)
{
unsigned long flags;
local_irq_save(&flags);
local_irq_restore(&flags);
}
static __noinline void local_irq_balance_n(void)
{
unsigned long flags1;
unsigned long flags2;
unsigned long flags3;
local_irq_save(&flags1);
local_irq_save(&flags2);
local_irq_save(&flags3);
local_irq_restore(&flags3);
local_irq_restore(&flags2);
local_irq_restore(&flags1);
}
SEC("?tc")
__success
int irq_balance_subprog(struct __sk_buff *ctx)
{
local_irq_balance();
return 0;
}
SEC("?fentry.s/" SYS_PREFIX "sys_getpgid")
__failure __msg("sleepable helper bpf_copy_from_user#")
int irq_sleepable_helper(void *ctx)
{
unsigned long flags;
u32 data;
local_irq_save(&flags);
bpf_copy_from_user(&data, sizeof(data), NULL);
local_irq_restore(&flags);
return 0;
}
SEC("?fentry.s/" SYS_PREFIX "sys_getpgid")
__failure __msg("kernel func bpf_copy_from_user_str is sleepable within IRQ-disabled region")
int irq_sleepable_kfunc(void *ctx)
{
unsigned long flags;
u32 data;
local_irq_save(&flags);
bpf_copy_from_user_str(&data, sizeof(data), NULL, 0);
local_irq_restore(&flags);
return 0;
}
int __noinline global_local_irq_balance(void)
{
local_irq_balance_n();
return 0;
}
SEC("?tc")
__failure __msg("global function calls are not allowed with IRQs disabled")
int irq_global_subprog(struct __sk_buff *ctx)
{
unsigned long flags;
bpf_local_irq_save(&flags);
global_local_irq_balance();
bpf_local_irq_restore(&flags);
return 0;
}
SEC("?tc")
__failure __msg("cannot restore irq state out of order")
int irq_restore_ooo(struct __sk_buff *ctx)
{
unsigned long flags1;
unsigned long flags2;
bpf_local_irq_save(&flags1);
bpf_local_irq_save(&flags2);
bpf_local_irq_restore(&flags1);
bpf_local_irq_restore(&flags2);
return 0;
}
SEC("?tc")
__failure __msg("cannot restore irq state out of order")
int irq_restore_ooo_3(struct __sk_buff *ctx)
{
unsigned long flags1;
unsigned long flags2;
unsigned long flags3;
bpf_local_irq_save(&flags1);
bpf_local_irq_save(&flags2);
bpf_local_irq_restore(&flags2);
bpf_local_irq_save(&flags3);
bpf_local_irq_restore(&flags1);
bpf_local_irq_restore(&flags3);
return 0;
}
static __noinline void local_irq_save_3(unsigned long *flags1, unsigned long *flags2,
unsigned long *flags3)
{
local_irq_save(flags1);
local_irq_save(flags2);
local_irq_save(flags3);
}
SEC("?tc")
__success
int irq_restore_3_subprog(struct __sk_buff *ctx)
{
unsigned long flags1;
unsigned long flags2;
unsigned long flags3;
local_irq_save_3(&flags1, &flags2, &flags3);
bpf_local_irq_restore(&flags3);
bpf_local_irq_restore(&flags2);
bpf_local_irq_restore(&flags1);
return 0;
}
SEC("?tc")
__failure __msg("cannot restore irq state out of order")
int irq_restore_4_subprog(struct __sk_buff *ctx)
{
unsigned long flags1;
unsigned long flags2;
unsigned long flags3;
unsigned long flags4;
local_irq_save_3(&flags1, &flags2, &flags3);
bpf_local_irq_restore(&flags3);
bpf_local_irq_save(&flags4);
bpf_local_irq_restore(&flags4);
bpf_local_irq_restore(&flags1);
return 0;
}
SEC("?tc")
__failure __msg("cannot restore irq state out of order")
int irq_restore_ooo_3_subprog(struct __sk_buff *ctx)
{
unsigned long flags1;
unsigned long flags2;
unsigned long flags3;
local_irq_save_3(&flags1, &flags2, &flags3);
bpf_local_irq_restore(&flags3);
bpf_local_irq_restore(&flags2);
bpf_local_irq_save(&flags3);
bpf_local_irq_restore(&flags1);
return 0;
}
SEC("?tc")
__failure __msg("expected an initialized")
int irq_restore_invalid(struct __sk_buff *ctx)
{
unsigned long flags1;
unsigned long flags = 0xfaceb00c;
bpf_local_irq_save(&flags1);
bpf_local_irq_restore(&flags);
return 0;
}
SEC("?tc")
__failure __msg("expected uninitialized")
int irq_save_invalid(struct __sk_buff *ctx)
{
unsigned long flags1;
bpf_local_irq_save(&flags1);
bpf_local_irq_save(&flags1);
return 0;
}
SEC("?tc")
__failure __msg("expected an initialized")
int irq_restore_iter(struct __sk_buff *ctx)
{
struct bpf_iter_num it;
bpf_iter_num_new(&it, 0, 42);
bpf_local_irq_restore((unsigned long *)&it);
return 0;
}
SEC("?tc")
__failure __msg("Unreleased reference id=1")
int irq_save_iter(struct __sk_buff *ctx)
{
struct bpf_iter_num it;
/* Ensure same sized slot has st->ref_obj_id set, so we reject based on
* slot_type != STACK_IRQ_FLAG...
*/
_Static_assert(sizeof(it) == sizeof(unsigned long), "broken iterator size");
bpf_iter_num_new(&it, 0, 42);
bpf_local_irq_save((unsigned long *)&it);
bpf_local_irq_restore((unsigned long *)&it);
return 0;
}
SEC("?tc")
__failure __msg("expected an initialized")
int irq_flag_overwrite(struct __sk_buff *ctx)
{
unsigned long flags;
bpf_local_irq_save(&flags);
flags = 0xdeadbeef;
bpf_local_irq_restore(&flags);
return 0;
}
SEC("?tc")
__failure __msg("expected an initialized")
int irq_flag_overwrite_partial(struct __sk_buff *ctx)
{
unsigned long flags;
bpf_local_irq_save(&flags);
*(((char *)&flags) + 1) = 0xff;
bpf_local_irq_restore(&flags);
return 0;
}
SEC("?tc")
__failure __msg("cannot restore irq state out of order")
int irq_ooo_refs_array(struct __sk_buff *ctx)
{
unsigned long flags[4];
struct { int i; } *p;
/* refs=1 */
bpf_local_irq_save(&flags[0]);
/* refs=1,2 */
p = bpf_obj_new(typeof(*p));
if (!p) {
bpf_local_irq_restore(&flags[0]);
return 0;
}
/* refs=1,2,3 */
bpf_local_irq_save(&flags[1]);
/* refs=1,2,3,4 */
bpf_local_irq_save(&flags[2]);
/* Now when we remove ref=2, the verifier must not break the ordering in
* the refs array between 1,3,4. With an older implementation, the
* verifier would swap the last element with the removed element, but to
* maintain the stack property we need to use memmove.
*/
bpf_obj_drop(p);
/* Save and restore to reset active_irq_id to 3, as the ordering is now
* refs=1,4,3. When restoring the linear scan will find prev_id in order
* as 3 instead of 4.
*/
bpf_local_irq_save(&flags[3]);
bpf_local_irq_restore(&flags[3]);
/* With the incorrect implementation, we can release flags[1], flags[2],
* and flags[0], i.e. in the wrong order.
*/
bpf_local_irq_restore(&flags[1]);
bpf_local_irq_restore(&flags[2]);
bpf_local_irq_restore(&flags[0]);
return 0;
}
char _license[] SEC("license") = "GPL";

View File

@ -4,7 +4,7 @@
#include "bpf_experimental.h"
#include <bpf/bpf_helpers.h>
#include "bpf_misc.h"
#include "../bpf_testmod/bpf_testmod_kfunc.h"
#include "../test_kmods/bpf_testmod_kfunc.h"
char _license[] SEC("license") = "GPL";

View File

@ -3,7 +3,7 @@
#include <vmlinux.h>
#include <bpf/bpf_tracing.h>
#include <bpf/bpf_helpers.h>
#include "../bpf_testmod/bpf_testmod_kfunc.h"
#include "../test_kmods/bpf_testmod_kfunc.h"
static struct prog_test_ref_kfunc __kptr *v;
long total_sum = -1;

View File

@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include <vmlinux.h>
#include <bpf/bpf_helpers.h>
#include "../bpf_testmod/bpf_testmod_kfunc.h"
#include "../test_kmods/bpf_testmod_kfunc.h"
SEC("tc")
int kfunc_destructive_test(void)

View File

@ -2,7 +2,7 @@
/* Copyright (c) 2021 Facebook */
#include <vmlinux.h>
#include <bpf/bpf_helpers.h>
#include "../bpf_testmod/bpf_testmod_kfunc.h"
#include "../test_kmods/bpf_testmod_kfunc.h"
struct syscall_test_args {
__u8 data[16];

View File

@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include <vmlinux.h>
#include <bpf/bpf_helpers.h>
#include "../bpf_testmod/bpf_testmod_kfunc.h"
#include "../test_kmods/bpf_testmod_kfunc.h"
SEC("tc")
int kfunc_call_fail(struct __sk_buff *ctx)

View File

@ -2,7 +2,7 @@
/* Copyright (c) 2021 Facebook */
#include <vmlinux.h>
#include <bpf/bpf_helpers.h>
#include "../bpf_testmod/bpf_testmod_kfunc.h"
#include "../test_kmods/bpf_testmod_kfunc.h"
SEC("tc")
int kfunc_call_test4(struct __sk_buff *skb)

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2021 Facebook */
#include "../bpf_testmod/bpf_testmod_kfunc.h"
#include "../test_kmods/bpf_testmod_kfunc.h"
extern const int bpf_prog_active __ksym;
int active_res = -1;

View File

@ -6,7 +6,7 @@
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_core_read.h>
#include "../bpf_experimental.h"
#include "../bpf_testmod/bpf_testmod_kfunc.h"
#include "../test_kmods/bpf_testmod_kfunc.h"
struct plain_local;

View File

@ -2,7 +2,7 @@
#include <vmlinux.h>
#include <bpf/bpf_tracing.h>
#include <bpf/bpf_helpers.h>
#include "../bpf_testmod/bpf_testmod_kfunc.h"
#include "../test_kmods/bpf_testmod_kfunc.h"
struct map_value {
struct prog_test_ref_kfunc __kptr_untrusted *unref_ptr;

View File

@ -4,7 +4,7 @@
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_core_read.h>
#include "bpf_misc.h"
#include "../bpf_testmod/bpf_testmod_kfunc.h"
#include "../test_kmods/bpf_testmod_kfunc.h"
struct map_value {
char buf[8];

View File

@ -2,7 +2,7 @@
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include "../bpf_testmod/bpf_testmod_kfunc.h"
#include "../test_kmods/bpf_testmod_kfunc.h"
char _license[] SEC("license") = "GPL";

View File

@ -2,7 +2,7 @@
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include "../bpf_testmod/bpf_testmod_kfunc.h"
#include "../test_kmods/bpf_testmod_kfunc.h"
char _license[] SEC("license") = "GPL";

View File

@ -4,7 +4,7 @@
#include <bpf/bpf_tracing.h>
#include <bpf/bpf_helpers.h>
#include "bpf_misc.h"
#include "../bpf_testmod/bpf_testmod_kfunc.h"
#include "../test_kmods/bpf_testmod_kfunc.h"
char _license[] SEC("license") = "GPL";

View File

@ -5,8 +5,10 @@
#include "bpf_misc.h"
#include "bpf_experimental.h"
extern int bpf_copy_from_user_str(void *dst, u32 dst__sz, const void *unsafe_ptr__ign, u64 flags) __weak __ksym;
SEC("?tc")
__failure __msg("BPF_EXIT instruction cannot be used inside bpf_preempt_disable-ed region")
__failure __msg("BPF_EXIT instruction in main prog cannot be used inside bpf_preempt_disable-ed region")
int preempt_lock_missing_1(struct __sk_buff *ctx)
{
bpf_preempt_disable();
@ -14,7 +16,7 @@ int preempt_lock_missing_1(struct __sk_buff *ctx)
}
SEC("?tc")
__failure __msg("BPF_EXIT instruction cannot be used inside bpf_preempt_disable-ed region")
__failure __msg("BPF_EXIT instruction in main prog cannot be used inside bpf_preempt_disable-ed region")
int preempt_lock_missing_2(struct __sk_buff *ctx)
{
bpf_preempt_disable();
@ -23,7 +25,7 @@ int preempt_lock_missing_2(struct __sk_buff *ctx)
}
SEC("?tc")
__failure __msg("BPF_EXIT instruction cannot be used inside bpf_preempt_disable-ed region")
__failure __msg("BPF_EXIT instruction in main prog cannot be used inside bpf_preempt_disable-ed region")
int preempt_lock_missing_3(struct __sk_buff *ctx)
{
bpf_preempt_disable();
@ -33,7 +35,7 @@ int preempt_lock_missing_3(struct __sk_buff *ctx)
}
SEC("?tc")
__failure __msg("BPF_EXIT instruction cannot be used inside bpf_preempt_disable-ed region")
__failure __msg("BPF_EXIT instruction in main prog cannot be used inside bpf_preempt_disable-ed region")
int preempt_lock_missing_3_minus_2(struct __sk_buff *ctx)
{
bpf_preempt_disable();
@ -55,7 +57,7 @@ static __noinline void preempt_enable(void)
}
SEC("?tc")
__failure __msg("BPF_EXIT instruction cannot be used inside bpf_preempt_disable-ed region")
__failure __msg("BPF_EXIT instruction in main prog cannot be used inside bpf_preempt_disable-ed region")
int preempt_lock_missing_1_subprog(struct __sk_buff *ctx)
{
preempt_disable();
@ -63,7 +65,7 @@ int preempt_lock_missing_1_subprog(struct __sk_buff *ctx)
}
SEC("?tc")
__failure __msg("BPF_EXIT instruction cannot be used inside bpf_preempt_disable-ed region")
__failure __msg("BPF_EXIT instruction in main prog cannot be used inside bpf_preempt_disable-ed region")
int preempt_lock_missing_2_subprog(struct __sk_buff *ctx)
{
preempt_disable();
@ -72,7 +74,7 @@ int preempt_lock_missing_2_subprog(struct __sk_buff *ctx)
}
SEC("?tc")
__failure __msg("BPF_EXIT instruction cannot be used inside bpf_preempt_disable-ed region")
__failure __msg("BPF_EXIT instruction in main prog cannot be used inside bpf_preempt_disable-ed region")
int preempt_lock_missing_2_minus_1_subprog(struct __sk_buff *ctx)
{
preempt_disable();
@ -113,6 +115,18 @@ int preempt_sleepable_helper(void *ctx)
return 0;
}
SEC("?fentry.s/" SYS_PREFIX "sys_getpgid")
__failure __msg("kernel func bpf_copy_from_user_str is sleepable within non-preemptible region")
int preempt_sleepable_kfunc(void *ctx)
{
u32 data;
bpf_preempt_disable();
bpf_copy_from_user_str(&data, sizeof(data), NULL, 0);
bpf_preempt_enable();
return 0;
}
int __noinline preempt_global_subprog(void)
{
preempt_balance_subprog();

View File

@ -4,8 +4,8 @@
#include <vmlinux.h>
#include <bpf/bpf_tracing.h>
#include "bpf_misc.h"
#include "../bpf_testmod/bpf_testmod.h"
#include "../bpf_testmod/bpf_testmod_kfunc.h"
#include "../test_kmods/bpf_testmod.h"
#include "../test_kmods/bpf_testmod_kfunc.h"
char _license[] SEC("license") = "GPL";

View File

@ -4,8 +4,8 @@
#include <vmlinux.h>
#include <bpf/bpf_tracing.h>
#include "bpf_misc.h"
#include "../bpf_testmod/bpf_testmod.h"
#include "../bpf_testmod/bpf_testmod_kfunc.h"
#include "../test_kmods/bpf_testmod.h"
#include "../test_kmods/bpf_testmod_kfunc.h"
char _license[] SEC("license") = "GPL";

View File

@ -2,7 +2,7 @@
/* Copyright (c) 2024 Google LLC */
#include <vmlinux.h>
#include <bpf/bpf_helpers.h>
#include "../bpf_testmod/bpf_testmod_kfunc.h"
#include "../test_kmods/bpf_testmod_kfunc.h"
SEC("syscall")
int init_sock(struct init_sock_args *args)

View File

@ -2,7 +2,7 @@
/* Copyright (c) 2024 Meta Platforms, Inc. and affiliates. */
#include <vmlinux.h>
#include <bpf/bpf_helpers.h>
#include "../bpf_testmod/bpf_testmod.h"
#include "../test_kmods/bpf_testmod.h"
char _license[] SEC("license") = "GPL";

View File

@ -2,7 +2,7 @@
/* Copyright (c) 2024 Meta Platforms, Inc. and affiliates. */
#include <vmlinux.h>
#include <bpf/bpf_tracing.h>
#include "../bpf_testmod/bpf_testmod.h"
#include "../test_kmods/bpf_testmod.h"
char _license[] SEC("license") = "GPL";

View File

@ -2,7 +2,7 @@
/* Copyright (c) 2024 Meta Platforms, Inc. and affiliates. */
#include <vmlinux.h>
#include <bpf/bpf_tracing.h>
#include "../bpf_testmod/bpf_testmod.h"
#include "../test_kmods/bpf_testmod.h"
char _license[] SEC("license") = "GPL";

View File

@ -2,7 +2,7 @@
/* Copyright (c) 2024 Meta Platforms, Inc. and affiliates. */
#include <vmlinux.h>
#include <bpf/bpf_tracing.h>
#include "../bpf_testmod/bpf_testmod.h"
#include "../test_kmods/bpf_testmod.h"
char _license[] SEC("license") = "GPL";

View File

@ -3,7 +3,7 @@
#include <vmlinux.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include "../bpf_testmod/bpf_testmod.h"
#include "../test_kmods/bpf_testmod.h"
char _license[] SEC("license") = "GPL";

View File

@ -3,7 +3,7 @@
#include <vmlinux.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include "../bpf_testmod/bpf_testmod.h"
#include "../test_kmods/bpf_testmod.h"
char _license[] SEC("license") = "GPL";

View File

@ -2,7 +2,7 @@
/* Copyright (c) 2024 Meta Platforms, Inc. and affiliates. */
#include <vmlinux.h>
#include <bpf/bpf_tracing.h>
#include "../bpf_testmod/bpf_testmod.h"
#include "../test_kmods/bpf_testmod.h"
char _license[] SEC("license") = "GPL";

View File

@ -3,7 +3,7 @@
#include <vmlinux.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include "../bpf_testmod/bpf_testmod.h"
#include "../test_kmods/bpf_testmod.h"
char _license[] SEC("license") = "GPL";

View File

@ -3,7 +3,7 @@
#include <vmlinux.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include "../bpf_testmod/bpf_testmod.h"
#include "../test_kmods/bpf_testmod.h"
char _license[] SEC("license") = "GPL";

View File

@ -3,7 +3,7 @@
#include <vmlinux.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include "../bpf_testmod/bpf_testmod.h"
#include "../test_kmods/bpf_testmod.h"
char _license[] SEC("license") = "GPL";

View File

@ -76,9 +76,9 @@ static int btf_load(void)
.magic = BTF_MAGIC,
.version = BTF_VERSION,
.hdr_len = sizeof(struct btf_header),
.type_len = sizeof(__u32) * 8,
.str_off = sizeof(__u32) * 8,
.str_len = sizeof(__u32),
.type_len = sizeof(raw_btf.types),
.str_off = offsetof(struct btf_blob, str) - offsetof(struct btf_blob, types),
.str_len = sizeof(raw_btf.str),
},
.types = {
/* long */

View File

@ -15,7 +15,7 @@
#include <linux/ipv6.h>
#include <linux/pkt_cls.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#include <netinet/udp.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_endian.h>

View File

@ -10,7 +10,7 @@
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/udp.h>
#include <netinet/udp.h>
/* offsetof() is used in static asserts, and the libbpf-redefined CO-RE
* friendly version breaks compilation for older clang versions <= 15

View File

@ -15,7 +15,7 @@
#include <linux/ipv6.h>
#include <linux/pkt_cls.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#include <netinet/udp.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_endian.h>

View File

@ -6,13 +6,20 @@
#include <stdbool.h>
extern bool CONFIG_X86_KERNEL_IBT __kconfig __weak;
extern bool CONFIG_PPC_FTRACE_OUT_OF_LINE __kconfig __weak;
extern bool CONFIG_KPROBES_ON_FTRACE __kconfig __weak;
extern bool CONFIG_PPC64 __kconfig __weak;
/* This function is here to have CONFIG_X86_KERNEL_IBT
* used and added to object BTF.
/* This function is here to have CONFIG_X86_KERNEL_IBT,
* CONFIG_PPC_FTRACE_OUT_OF_LINE, CONFIG_KPROBES_ON_FTRACE,
* CONFIG_PPC6 used and added to object BTF.
*/
int unused(void)
{
return CONFIG_X86_KERNEL_IBT ? 0 : 1;
return CONFIG_X86_KERNEL_IBT ||
CONFIG_PPC_FTRACE_OUT_OF_LINE ||
CONFIG_KPROBES_ON_FTRACE ||
CONFIG_PPC64 ? 0 : 1;
}
SEC("kprobe")

View File

@ -4,7 +4,7 @@
#include <bpf/bpf_helpers.h>
#include "bpf_misc.h"
#include "bpf_kfuncs.h"
#include "../bpf_testmod/bpf_testmod_kfunc.h"
#include "../test_kmods/bpf_testmod_kfunc.h"
SEC("tc")
int kfunc_dynptr_nullable_test1(struct __sk_buff *skb)

View File

@ -5,7 +5,7 @@
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include <bpf/bpf_core_read.h>
#include "../bpf_testmod/bpf_testmod.h"
#include "../test_kmods/bpf_testmod.h"
__u32 raw_tp_read_sz = 0;

View File

@ -3,7 +3,7 @@
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include "../bpf_testmod/bpf_testmod.h"
#include "../test_kmods/bpf_testmod.h"
#include "bpf_misc.h"
SEC("tp_btf/bpf_testmod_test_nullable_bare")

View File

@ -8,7 +8,7 @@
#define round_up(x, y) ((((x) - 1) | __round_mask(x, y)) + 1)
#define ctx_ptr(ctx, mem) (void *)(unsigned long)ctx->mem
SEC("t")
SEC("tc")
int ing_cls(struct __sk_buff *ctx)
{
__u8 *data, *data_meta, *data_end;
@ -28,7 +28,7 @@ int ing_cls(struct __sk_buff *ctx)
return diff ? TC_ACT_SHOT : TC_ACT_OK;
}
SEC("x")
SEC("xdp")
int ing_xdp(struct xdp_md *ctx)
{
__u8 *data, *data_meta, *data_end;

View File

@ -4,7 +4,7 @@
#include <vmlinux.h>
#include <bpf/bpf_tracing.h>
#include "bpf_misc.h"
#include "../bpf_testmod/bpf_testmod.h"
#include "../test_kmods/bpf_testmod.h"
char _license[] SEC("license") = "GPL";

View File

@ -187,7 +187,7 @@ l0_%=: r6 = r0; \
SEC("cgroup/skb")
__description("spin_lock: test6 missing unlock")
__failure __msg("BPF_EXIT instruction cannot be used inside bpf_spin_lock-ed region")
__failure __msg("BPF_EXIT instruction in main prog cannot be used inside bpf_spin_lock-ed region")
__failure_unpriv __msg_unpriv("")
__naked void spin_lock_test6_missing_unlock(void)
{

View File

@ -5,7 +5,7 @@
#include "bpf_experimental.h"
#include <bpf/bpf_helpers.h>
#include "bpf_misc.h"
#include "../bpf_testmod/bpf_testmod_kfunc.h"
#include "../test_kmods/bpf_testmod_kfunc.h"
char _license[] SEC("license") = "GPL";

View File

@ -5,7 +5,7 @@
#include "bpf_experimental.h"
#include <bpf/bpf_helpers.h>
#include "bpf_misc.h"
#include "../bpf_testmod/bpf_testmod_kfunc.h"
#include "../test_kmods/bpf_testmod_kfunc.h"
char _license[] SEC("license") = "GPL";

View File

@ -1,780 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Inject packets with all sorts of encapsulation into the kernel.
*
* IPv4/IPv6 outer layer 3
* GRE/GUE/BARE outer layer 4, where bare is IPIP/SIT/IPv4-in-IPv6/..
* IPv4/IPv6 inner layer 3
*/
#define _GNU_SOURCE
#include <stddef.h>
#include <arpa/inet.h>
#include <asm/byteorder.h>
#include <error.h>
#include <errno.h>
#include <linux/if_packet.h>
#include <linux/if_ether.h>
#include <linux/ipv6.h>
#include <netinet/ip.h>
#include <netinet/in.h>
#include <netinet/udp.h>
#include <poll.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#define CFG_PORT_INNER 8000
/* Add some protocol definitions that do not exist in userspace */
struct grehdr {
uint16_t unused;
uint16_t protocol;
} __attribute__((packed));
struct guehdr {
union {
struct {
#if defined(__LITTLE_ENDIAN_BITFIELD)
__u8 hlen:5,
control:1,
version:2;
#elif defined (__BIG_ENDIAN_BITFIELD)
__u8 version:2,
control:1,
hlen:5;
#else
#error "Please fix <asm/byteorder.h>"
#endif
__u8 proto_ctype;
__be16 flags;
};
__be32 word;
};
};
static uint8_t cfg_dsfield_inner;
static uint8_t cfg_dsfield_outer;
static uint8_t cfg_encap_proto;
static bool cfg_expect_failure = false;
static int cfg_l3_extra = AF_UNSPEC; /* optional SIT prefix */
static int cfg_l3_inner = AF_UNSPEC;
static int cfg_l3_outer = AF_UNSPEC;
static int cfg_num_pkt = 10;
static int cfg_num_secs = 0;
static char cfg_payload_char = 'a';
static int cfg_payload_len = 100;
static int cfg_port_gue = 6080;
static bool cfg_only_rx;
static bool cfg_only_tx;
static int cfg_src_port = 9;
static char buf[ETH_DATA_LEN];
#define INIT_ADDR4(name, addr4, port) \
static struct sockaddr_in name = { \
.sin_family = AF_INET, \
.sin_port = __constant_htons(port), \
.sin_addr.s_addr = __constant_htonl(addr4), \
};
#define INIT_ADDR6(name, addr6, port) \
static struct sockaddr_in6 name = { \
.sin6_family = AF_INET6, \
.sin6_port = __constant_htons(port), \
.sin6_addr = addr6, \
};
INIT_ADDR4(in_daddr4, INADDR_LOOPBACK, CFG_PORT_INNER)
INIT_ADDR4(in_saddr4, INADDR_LOOPBACK + 2, 0)
INIT_ADDR4(out_daddr4, INADDR_LOOPBACK, 0)
INIT_ADDR4(out_saddr4, INADDR_LOOPBACK + 1, 0)
INIT_ADDR4(extra_daddr4, INADDR_LOOPBACK, 0)
INIT_ADDR4(extra_saddr4, INADDR_LOOPBACK + 1, 0)
INIT_ADDR6(in_daddr6, IN6ADDR_LOOPBACK_INIT, CFG_PORT_INNER)
INIT_ADDR6(in_saddr6, IN6ADDR_LOOPBACK_INIT, 0)
INIT_ADDR6(out_daddr6, IN6ADDR_LOOPBACK_INIT, 0)
INIT_ADDR6(out_saddr6, IN6ADDR_LOOPBACK_INIT, 0)
INIT_ADDR6(extra_daddr6, IN6ADDR_LOOPBACK_INIT, 0)
INIT_ADDR6(extra_saddr6, IN6ADDR_LOOPBACK_INIT, 0)
static unsigned long util_gettime(void)
{
struct timeval tv;
gettimeofday(&tv, NULL);
return (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
}
static void util_printaddr(const char *msg, struct sockaddr *addr)
{
unsigned long off = 0;
char nbuf[INET6_ADDRSTRLEN];
switch (addr->sa_family) {
case PF_INET:
off = __builtin_offsetof(struct sockaddr_in, sin_addr);
break;
case PF_INET6:
off = __builtin_offsetof(struct sockaddr_in6, sin6_addr);
break;
default:
error(1, 0, "printaddr: unsupported family %u\n",
addr->sa_family);
}
if (!inet_ntop(addr->sa_family, ((void *) addr) + off, nbuf,
sizeof(nbuf)))
error(1, errno, "inet_ntop");
fprintf(stderr, "%s: %s\n", msg, nbuf);
}
static unsigned long add_csum_hword(const uint16_t *start, int num_u16)
{
unsigned long sum = 0;
int i;
for (i = 0; i < num_u16; i++)
sum += start[i];
return sum;
}
static uint16_t build_ip_csum(const uint16_t *start, int num_u16,
unsigned long sum)
{
sum += add_csum_hword(start, num_u16);
while (sum >> 16)
sum = (sum & 0xffff) + (sum >> 16);
return ~sum;
}
static void build_ipv4_header(void *header, uint8_t proto,
uint32_t src, uint32_t dst,
int payload_len, uint8_t tos)
{
struct iphdr *iph = header;
iph->ihl = 5;
iph->version = 4;
iph->tos = tos;
iph->ttl = 8;
iph->tot_len = htons(sizeof(*iph) + payload_len);
iph->id = htons(1337);
iph->protocol = proto;
iph->saddr = src;
iph->daddr = dst;
iph->check = build_ip_csum((void *) iph, iph->ihl << 1, 0);
}
static void ipv6_set_dsfield(struct ipv6hdr *ip6h, uint8_t dsfield)
{
uint16_t val, *ptr = (uint16_t *)ip6h;
val = ntohs(*ptr);
val &= 0xF00F;
val |= ((uint16_t) dsfield) << 4;
*ptr = htons(val);
}
static void build_ipv6_header(void *header, uint8_t proto,
struct sockaddr_in6 *src,
struct sockaddr_in6 *dst,
int payload_len, uint8_t dsfield)
{
struct ipv6hdr *ip6h = header;
ip6h->version = 6;
ip6h->payload_len = htons(payload_len);
ip6h->nexthdr = proto;
ip6h->hop_limit = 8;
ipv6_set_dsfield(ip6h, dsfield);
memcpy(&ip6h->saddr, &src->sin6_addr, sizeof(ip6h->saddr));
memcpy(&ip6h->daddr, &dst->sin6_addr, sizeof(ip6h->daddr));
}
static uint16_t build_udp_v4_csum(const struct iphdr *iph,
const struct udphdr *udph,
int num_words)
{
unsigned long pseudo_sum;
int num_u16 = sizeof(iph->saddr); /* halfwords: twice byte len */
pseudo_sum = add_csum_hword((void *) &iph->saddr, num_u16);
pseudo_sum += htons(IPPROTO_UDP);
pseudo_sum += udph->len;
return build_ip_csum((void *) udph, num_words, pseudo_sum);
}
static uint16_t build_udp_v6_csum(const struct ipv6hdr *ip6h,
const struct udphdr *udph,
int num_words)
{
unsigned long pseudo_sum;
int num_u16 = sizeof(ip6h->saddr); /* halfwords: twice byte len */
pseudo_sum = add_csum_hword((void *) &ip6h->saddr, num_u16);
pseudo_sum += htons(ip6h->nexthdr);
pseudo_sum += ip6h->payload_len;
return build_ip_csum((void *) udph, num_words, pseudo_sum);
}
static void build_udp_header(void *header, int payload_len,
uint16_t dport, int family)
{
struct udphdr *udph = header;
int len = sizeof(*udph) + payload_len;
udph->source = htons(cfg_src_port);
udph->dest = htons(dport);
udph->len = htons(len);
udph->check = 0;
if (family == AF_INET)
udph->check = build_udp_v4_csum(header - sizeof(struct iphdr),
udph, len >> 1);
else
udph->check = build_udp_v6_csum(header - sizeof(struct ipv6hdr),
udph, len >> 1);
}
static void build_gue_header(void *header, uint8_t proto)
{
struct guehdr *gueh = header;
gueh->proto_ctype = proto;
}
static void build_gre_header(void *header, uint16_t proto)
{
struct grehdr *greh = header;
greh->protocol = htons(proto);
}
static int l3_length(int family)
{
if (family == AF_INET)
return sizeof(struct iphdr);
else
return sizeof(struct ipv6hdr);
}
static int build_packet(void)
{
int ol3_len = 0, ol4_len = 0, il3_len = 0, il4_len = 0;
int el3_len = 0;
if (cfg_l3_extra)
el3_len = l3_length(cfg_l3_extra);
/* calculate header offsets */
if (cfg_encap_proto) {
ol3_len = l3_length(cfg_l3_outer);
if (cfg_encap_proto == IPPROTO_GRE)
ol4_len = sizeof(struct grehdr);
else if (cfg_encap_proto == IPPROTO_UDP)
ol4_len = sizeof(struct udphdr) + sizeof(struct guehdr);
}
il3_len = l3_length(cfg_l3_inner);
il4_len = sizeof(struct udphdr);
if (el3_len + ol3_len + ol4_len + il3_len + il4_len + cfg_payload_len >=
sizeof(buf))
error(1, 0, "packet too large\n");
/*
* Fill packet from inside out, to calculate correct checksums.
* But create ip before udp headers, as udp uses ip for pseudo-sum.
*/
memset(buf + el3_len + ol3_len + ol4_len + il3_len + il4_len,
cfg_payload_char, cfg_payload_len);
/* add zero byte for udp csum padding */
buf[el3_len + ol3_len + ol4_len + il3_len + il4_len + cfg_payload_len] = 0;
switch (cfg_l3_inner) {
case PF_INET:
build_ipv4_header(buf + el3_len + ol3_len + ol4_len,
IPPROTO_UDP,
in_saddr4.sin_addr.s_addr,
in_daddr4.sin_addr.s_addr,
il4_len + cfg_payload_len,
cfg_dsfield_inner);
break;
case PF_INET6:
build_ipv6_header(buf + el3_len + ol3_len + ol4_len,
IPPROTO_UDP,
&in_saddr6, &in_daddr6,
il4_len + cfg_payload_len,
cfg_dsfield_inner);
break;
}
build_udp_header(buf + el3_len + ol3_len + ol4_len + il3_len,
cfg_payload_len, CFG_PORT_INNER, cfg_l3_inner);
if (!cfg_encap_proto)
return il3_len + il4_len + cfg_payload_len;
switch (cfg_l3_outer) {
case PF_INET:
build_ipv4_header(buf + el3_len, cfg_encap_proto,
out_saddr4.sin_addr.s_addr,
out_daddr4.sin_addr.s_addr,
ol4_len + il3_len + il4_len + cfg_payload_len,
cfg_dsfield_outer);
break;
case PF_INET6:
build_ipv6_header(buf + el3_len, cfg_encap_proto,
&out_saddr6, &out_daddr6,
ol4_len + il3_len + il4_len + cfg_payload_len,
cfg_dsfield_outer);
break;
}
switch (cfg_encap_proto) {
case IPPROTO_UDP:
build_gue_header(buf + el3_len + ol3_len + ol4_len -
sizeof(struct guehdr),
cfg_l3_inner == PF_INET ? IPPROTO_IPIP
: IPPROTO_IPV6);
build_udp_header(buf + el3_len + ol3_len,
sizeof(struct guehdr) + il3_len + il4_len +
cfg_payload_len,
cfg_port_gue, cfg_l3_outer);
break;
case IPPROTO_GRE:
build_gre_header(buf + el3_len + ol3_len,
cfg_l3_inner == PF_INET ? ETH_P_IP
: ETH_P_IPV6);
break;
}
switch (cfg_l3_extra) {
case PF_INET:
build_ipv4_header(buf,
cfg_l3_outer == PF_INET ? IPPROTO_IPIP
: IPPROTO_IPV6,
extra_saddr4.sin_addr.s_addr,
extra_daddr4.sin_addr.s_addr,
ol3_len + ol4_len + il3_len + il4_len +
cfg_payload_len, 0);
break;
case PF_INET6:
build_ipv6_header(buf,
cfg_l3_outer == PF_INET ? IPPROTO_IPIP
: IPPROTO_IPV6,
&extra_saddr6, &extra_daddr6,
ol3_len + ol4_len + il3_len + il4_len +
cfg_payload_len, 0);
break;
}
return el3_len + ol3_len + ol4_len + il3_len + il4_len +
cfg_payload_len;
}
/* sender transmits encapsulated over RAW or unencap'd over UDP */
static int setup_tx(void)
{
int family, fd, ret;
if (cfg_l3_extra)
family = cfg_l3_extra;
else if (cfg_l3_outer)
family = cfg_l3_outer;
else
family = cfg_l3_inner;
fd = socket(family, SOCK_RAW, IPPROTO_RAW);
if (fd == -1)
error(1, errno, "socket tx");
if (cfg_l3_extra) {
if (cfg_l3_extra == PF_INET)
ret = connect(fd, (void *) &extra_daddr4,
sizeof(extra_daddr4));
else
ret = connect(fd, (void *) &extra_daddr6,
sizeof(extra_daddr6));
if (ret)
error(1, errno, "connect tx");
} else if (cfg_l3_outer) {
/* connect to destination if not encapsulated */
if (cfg_l3_outer == PF_INET)
ret = connect(fd, (void *) &out_daddr4,
sizeof(out_daddr4));
else
ret = connect(fd, (void *) &out_daddr6,
sizeof(out_daddr6));
if (ret)
error(1, errno, "connect tx");
} else {
/* otherwise using loopback */
if (cfg_l3_inner == PF_INET)
ret = connect(fd, (void *) &in_daddr4,
sizeof(in_daddr4));
else
ret = connect(fd, (void *) &in_daddr6,
sizeof(in_daddr6));
if (ret)
error(1, errno, "connect tx");
}
return fd;
}
/* receiver reads unencapsulated UDP */
static int setup_rx(void)
{
int fd, ret;
fd = socket(cfg_l3_inner, SOCK_DGRAM, 0);
if (fd == -1)
error(1, errno, "socket rx");
if (cfg_l3_inner == PF_INET)
ret = bind(fd, (void *) &in_daddr4, sizeof(in_daddr4));
else
ret = bind(fd, (void *) &in_daddr6, sizeof(in_daddr6));
if (ret)
error(1, errno, "bind rx");
return fd;
}
static int do_tx(int fd, const char *pkt, int len)
{
int ret;
ret = write(fd, pkt, len);
if (ret == -1)
error(1, errno, "send");
if (ret != len)
error(1, errno, "send: len (%d < %d)\n", ret, len);
return 1;
}
static int do_poll(int fd, short events, int timeout)
{
struct pollfd pfd;
int ret;
pfd.fd = fd;
pfd.events = events;
ret = poll(&pfd, 1, timeout);
if (ret == -1)
error(1, errno, "poll");
if (ret && !(pfd.revents & POLLIN))
error(1, errno, "poll: unexpected event 0x%x\n", pfd.revents);
return ret;
}
static int do_rx(int fd)
{
char rbuf;
int ret, num = 0;
while (1) {
ret = recv(fd, &rbuf, 1, MSG_DONTWAIT);
if (ret == -1 && errno == EAGAIN)
break;
if (ret == -1)
error(1, errno, "recv");
if (rbuf != cfg_payload_char)
error(1, 0, "recv: payload mismatch");
num++;
}
return num;
}
static int do_main(void)
{
unsigned long tstop, treport, tcur;
int fdt = -1, fdr = -1, len, tx = 0, rx = 0;
if (!cfg_only_tx)
fdr = setup_rx();
if (!cfg_only_rx)
fdt = setup_tx();
len = build_packet();
tcur = util_gettime();
treport = tcur + 1000;
tstop = tcur + (cfg_num_secs * 1000);
while (1) {
if (!cfg_only_rx)
tx += do_tx(fdt, buf, len);
if (!cfg_only_tx)
rx += do_rx(fdr);
if (cfg_num_secs) {
tcur = util_gettime();
if (tcur >= tstop)
break;
if (tcur >= treport) {
fprintf(stderr, "pkts: tx=%u rx=%u\n", tx, rx);
tx = 0;
rx = 0;
treport = tcur + 1000;
}
} else {
if (tx == cfg_num_pkt)
break;
}
}
/* read straggler packets, if any */
if (rx < tx) {
tstop = util_gettime() + 100;
while (rx < tx) {
tcur = util_gettime();
if (tcur >= tstop)
break;
do_poll(fdr, POLLIN, tstop - tcur);
rx += do_rx(fdr);
}
}
fprintf(stderr, "pkts: tx=%u rx=%u\n", tx, rx);
if (fdr != -1 && close(fdr))
error(1, errno, "close rx");
if (fdt != -1 && close(fdt))
error(1, errno, "close tx");
/*
* success (== 0) only if received all packets
* unless failure is expected, in which case none must arrive.
*/
if (cfg_expect_failure)
return rx != 0;
else
return rx != tx;
}
static void __attribute__((noreturn)) usage(const char *filepath)
{
fprintf(stderr, "Usage: %s [-e gre|gue|bare|none] [-i 4|6] [-l len] "
"[-O 4|6] [-o 4|6] [-n num] [-t secs] [-R] [-T] "
"[-s <osrc> [-d <odst>] [-S <isrc>] [-D <idst>] "
"[-x <otos>] [-X <itos>] [-f <isport>] [-F]\n",
filepath);
exit(1);
}
static void parse_addr(int family, void *addr, const char *optarg)
{
int ret;
ret = inet_pton(family, optarg, addr);
if (ret == -1)
error(1, errno, "inet_pton");
if (ret == 0)
error(1, 0, "inet_pton: bad string");
}
static void parse_addr4(struct sockaddr_in *addr, const char *optarg)
{
parse_addr(AF_INET, &addr->sin_addr, optarg);
}
static void parse_addr6(struct sockaddr_in6 *addr, const char *optarg)
{
parse_addr(AF_INET6, &addr->sin6_addr, optarg);
}
static int parse_protocol_family(const char *filepath, const char *optarg)
{
if (!strcmp(optarg, "4"))
return PF_INET;
if (!strcmp(optarg, "6"))
return PF_INET6;
usage(filepath);
}
static void parse_opts(int argc, char **argv)
{
int c;
while ((c = getopt(argc, argv, "d:D:e:f:Fhi:l:n:o:O:Rs:S:t:Tx:X:")) != -1) {
switch (c) {
case 'd':
if (cfg_l3_outer == AF_UNSPEC)
error(1, 0, "-d must be preceded by -o");
if (cfg_l3_outer == AF_INET)
parse_addr4(&out_daddr4, optarg);
else
parse_addr6(&out_daddr6, optarg);
break;
case 'D':
if (cfg_l3_inner == AF_UNSPEC)
error(1, 0, "-D must be preceded by -i");
if (cfg_l3_inner == AF_INET)
parse_addr4(&in_daddr4, optarg);
else
parse_addr6(&in_daddr6, optarg);
break;
case 'e':
if (!strcmp(optarg, "gre"))
cfg_encap_proto = IPPROTO_GRE;
else if (!strcmp(optarg, "gue"))
cfg_encap_proto = IPPROTO_UDP;
else if (!strcmp(optarg, "bare"))
cfg_encap_proto = IPPROTO_IPIP;
else if (!strcmp(optarg, "none"))
cfg_encap_proto = IPPROTO_IP; /* == 0 */
else
usage(argv[0]);
break;
case 'f':
cfg_src_port = strtol(optarg, NULL, 0);
break;
case 'F':
cfg_expect_failure = true;
break;
case 'h':
usage(argv[0]);
break;
case 'i':
if (!strcmp(optarg, "4"))
cfg_l3_inner = PF_INET;
else if (!strcmp(optarg, "6"))
cfg_l3_inner = PF_INET6;
else
usage(argv[0]);
break;
case 'l':
cfg_payload_len = strtol(optarg, NULL, 0);
break;
case 'n':
cfg_num_pkt = strtol(optarg, NULL, 0);
break;
case 'o':
cfg_l3_outer = parse_protocol_family(argv[0], optarg);
break;
case 'O':
cfg_l3_extra = parse_protocol_family(argv[0], optarg);
break;
case 'R':
cfg_only_rx = true;
break;
case 's':
if (cfg_l3_outer == AF_INET)
parse_addr4(&out_saddr4, optarg);
else
parse_addr6(&out_saddr6, optarg);
break;
case 'S':
if (cfg_l3_inner == AF_INET)
parse_addr4(&in_saddr4, optarg);
else
parse_addr6(&in_saddr6, optarg);
break;
case 't':
cfg_num_secs = strtol(optarg, NULL, 0);
break;
case 'T':
cfg_only_tx = true;
break;
case 'x':
cfg_dsfield_outer = strtol(optarg, NULL, 0);
break;
case 'X':
cfg_dsfield_inner = strtol(optarg, NULL, 0);
break;
}
}
if (cfg_only_rx && cfg_only_tx)
error(1, 0, "options: cannot combine rx-only and tx-only");
if (cfg_encap_proto && cfg_l3_outer == AF_UNSPEC)
error(1, 0, "options: must specify outer with encap");
else if ((!cfg_encap_proto) && cfg_l3_outer != AF_UNSPEC)
error(1, 0, "options: cannot combine no-encap and outer");
else if ((!cfg_encap_proto) && cfg_l3_extra != AF_UNSPEC)
error(1, 0, "options: cannot combine no-encap and extra");
if (cfg_l3_inner == AF_UNSPEC)
cfg_l3_inner = AF_INET6;
if (cfg_l3_inner == AF_INET6 && cfg_encap_proto == IPPROTO_IPIP)
cfg_encap_proto = IPPROTO_IPV6;
/* RFC 6040 4.2:
* on decap, if outer encountered congestion (CE == 0x3),
* but inner cannot encode ECN (NoECT == 0x0), then drop packet.
*/
if (((cfg_dsfield_outer & 0x3) == 0x3) &&
((cfg_dsfield_inner & 0x3) == 0x0))
cfg_expect_failure = true;
}
static void print_opts(void)
{
if (cfg_l3_inner == PF_INET6) {
util_printaddr("inner.dest6", (void *) &in_daddr6);
util_printaddr("inner.source6", (void *) &in_saddr6);
} else {
util_printaddr("inner.dest4", (void *) &in_daddr4);
util_printaddr("inner.source4", (void *) &in_saddr4);
}
if (!cfg_l3_outer)
return;
fprintf(stderr, "encap proto: %u\n", cfg_encap_proto);
if (cfg_l3_outer == PF_INET6) {
util_printaddr("outer.dest6", (void *) &out_daddr6);
util_printaddr("outer.source6", (void *) &out_saddr6);
} else {
util_printaddr("outer.dest4", (void *) &out_daddr4);
util_printaddr("outer.source4", (void *) &out_saddr4);
}
if (!cfg_l3_extra)
return;
if (cfg_l3_outer == PF_INET6) {
util_printaddr("extra.dest6", (void *) &extra_daddr6);
util_printaddr("extra.source6", (void *) &extra_saddr6);
} else {
util_printaddr("extra.dest4", (void *) &extra_daddr4);
util_printaddr("extra.source4", (void *) &extra_saddr4);
}
}
int main(int argc, char **argv)
{
parse_opts(argc, argv);
print_opts();
return do_main();
}

View File

@ -1,178 +0,0 @@
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
#
# Load BPF flow dissector and verify it correctly dissects traffic
BPF_FILE="bpf_flow.bpf.o"
export TESTNAME=test_flow_dissector
unmount=0
# Kselftest framework requirement - SKIP code is 4.
ksft_skip=4
msg="skip all tests:"
if [ $UID != 0 ]; then
echo $msg please run this as root >&2
exit $ksft_skip
fi
# This test needs to be run in a network namespace with in_netns.sh. Check if
# this is the case and run it with in_netns.sh if it is being run in the root
# namespace.
if [[ -z $(ip netns identify $$) ]]; then
err=0
if bpftool="$(which bpftool)"; then
echo "Testing global flow dissector..."
$bpftool prog loadall $BPF_FILE /sys/fs/bpf/flow \
type flow_dissector
if ! unshare --net $bpftool prog attach pinned \
/sys/fs/bpf/flow/_dissect flow_dissector; then
echo "Unexpected unsuccessful attach in namespace" >&2
err=1
fi
$bpftool prog attach pinned /sys/fs/bpf/flow/_dissect \
flow_dissector
if unshare --net $bpftool prog attach pinned \
/sys/fs/bpf/flow/_dissect flow_dissector; then
echo "Unexpected successful attach in namespace" >&2
err=1
fi
if ! $bpftool prog detach pinned \
/sys/fs/bpf/flow/_dissect flow_dissector; then
echo "Failed to detach flow dissector" >&2
err=1
fi
rm -rf /sys/fs/bpf/flow
else
echo "Skipping root flow dissector test, bpftool not found" >&2
fi
# Run the rest of the tests in a net namespace.
../net/in_netns.sh "$0" "$@"
err=$(( $err + $? ))
if (( $err == 0 )); then
echo "selftests: $TESTNAME [PASS]";
else
echo "selftests: $TESTNAME [FAILED]";
fi
exit $err
fi
# Determine selftest success via shell exit code
exit_handler()
{
set +e
# Cleanup
tc filter del dev lo ingress pref 1337 2> /dev/null
tc qdisc del dev lo ingress 2> /dev/null
./flow_dissector_load -d 2> /dev/null
if [ $unmount -ne 0 ]; then
umount bpffs 2> /dev/null
fi
}
# Exit script immediately (well catched by trap handler) if any
# program/thing exits with a non-zero status.
set -e
# (Use 'trap -l' to list meaning of numbers)
trap exit_handler 0 2 3 6 9
# Mount BPF file system
if /bin/mount | grep /sys/fs/bpf > /dev/null; then
echo "bpffs already mounted"
else
echo "bpffs not mounted. Mounting..."
unmount=1
/bin/mount bpffs /sys/fs/bpf -t bpf
fi
# Attach BPF program
./flow_dissector_load -p $BPF_FILE -s _dissect
# Setup
tc qdisc add dev lo ingress
echo 0 > /proc/sys/net/ipv4/conf/default/rp_filter
echo 0 > /proc/sys/net/ipv4/conf/all/rp_filter
echo 0 > /proc/sys/net/ipv4/conf/lo/rp_filter
echo "Testing IPv4..."
# Drops all IP/UDP packets coming from port 9
tc filter add dev lo parent ffff: protocol ip pref 1337 flower ip_proto \
udp src_port 9 action drop
# Send 10 IPv4/UDP packets from port 8. Filter should not drop any.
./test_flow_dissector -i 4 -f 8
# Send 10 IPv4/UDP packets from port 9. Filter should drop all.
./test_flow_dissector -i 4 -f 9 -F
# Send 10 IPv4/UDP packets from port 10. Filter should not drop any.
./test_flow_dissector -i 4 -f 10
echo "Testing IPv4 from 127.0.0.127 (fallback to generic dissector)..."
# Send 10 IPv4/UDP packets from port 8. Filter should not drop any.
./test_flow_dissector -i 4 -S 127.0.0.127 -f 8
# Send 10 IPv4/UDP packets from port 9. Filter should drop all.
./test_flow_dissector -i 4 -S 127.0.0.127 -f 9 -F
# Send 10 IPv4/UDP packets from port 10. Filter should not drop any.
./test_flow_dissector -i 4 -S 127.0.0.127 -f 10
echo "Testing IPIP..."
# Send 10 IPv4/IPv4/UDP packets from port 8. Filter should not drop any.
./with_addr.sh ./with_tunnels.sh ./test_flow_dissector -o 4 -e bare -i 4 \
-D 192.168.0.1 -S 1.1.1.1 -f 8
# Send 10 IPv4/IPv4/UDP packets from port 9. Filter should drop all.
./with_addr.sh ./with_tunnels.sh ./test_flow_dissector -o 4 -e bare -i 4 \
-D 192.168.0.1 -S 1.1.1.1 -f 9 -F
# Send 10 IPv4/IPv4/UDP packets from port 10. Filter should not drop any.
./with_addr.sh ./with_tunnels.sh ./test_flow_dissector -o 4 -e bare -i 4 \
-D 192.168.0.1 -S 1.1.1.1 -f 10
echo "Testing IPv4 + GRE..."
# Send 10 IPv4/GRE/IPv4/UDP packets from port 8. Filter should not drop any.
./with_addr.sh ./with_tunnels.sh ./test_flow_dissector -o 4 -e gre -i 4 \
-D 192.168.0.1 -S 1.1.1.1 -f 8
# Send 10 IPv4/GRE/IPv4/UDP packets from port 9. Filter should drop all.
./with_addr.sh ./with_tunnels.sh ./test_flow_dissector -o 4 -e gre -i 4 \
-D 192.168.0.1 -S 1.1.1.1 -f 9 -F
# Send 10 IPv4/GRE/IPv4/UDP packets from port 10. Filter should not drop any.
./with_addr.sh ./with_tunnels.sh ./test_flow_dissector -o 4 -e gre -i 4 \
-D 192.168.0.1 -S 1.1.1.1 -f 10
tc filter del dev lo ingress pref 1337
echo "Testing port range..."
# Drops all IP/UDP packets coming from port 8-10
tc filter add dev lo parent ffff: protocol ip pref 1337 flower ip_proto \
udp src_port 8-10 action drop
# Send 10 IPv4/UDP packets from port 7. Filter should not drop any.
./test_flow_dissector -i 4 -f 7
# Send 10 IPv4/UDP packets from port 9. Filter should drop all.
./test_flow_dissector -i 4 -f 9 -F
# Send 10 IPv4/UDP packets from port 11. Filter should not drop any.
./test_flow_dissector -i 4 -f 11
tc filter del dev lo ingress pref 1337
echo "Testing IPv6..."
# Drops all IPv6/UDP packets coming from port 9
tc filter add dev lo parent ffff: protocol ipv6 pref 1337 flower ip_proto \
udp src_port 9 action drop
# Send 10 IPv6/UDP packets from port 8. Filter should not drop any.
./test_flow_dissector -i 6 -f 8
# Send 10 IPv6/UDP packets from port 9. Filter should drop all.
./test_flow_dissector -i 6 -f 9 -F
# Send 10 IPv6/UDP packets from port 10. Filter should not drop any.
./test_flow_dissector -i 6 -f 10
exit 0

Some files were not shown because too many files have changed in this diff Show More