mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2024-12-29 01:02:08 +00:00
Merge branch 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git
This commit is contained in:
commit
49e3ee1413
@ -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(u32 ufd);
|
||||||
struct bpf_map *bpf_map_get_with_uref(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)
|
static inline struct bpf_map *__bpf_map_get(struct fd f)
|
||||||
{
|
{
|
||||||
if (fd_empty(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;
|
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(struct bpf_map *map);
|
||||||
void bpf_map_inc_with_uref(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);
|
struct bpf_map *__bpf_map_inc_not_zero(struct bpf_map *map, bool uref);
|
||||||
|
@ -233,6 +233,7 @@ enum bpf_stack_slot_type {
|
|||||||
*/
|
*/
|
||||||
STACK_DYNPTR,
|
STACK_DYNPTR,
|
||||||
STACK_ITER,
|
STACK_ITER,
|
||||||
|
STACK_IRQ_FLAG,
|
||||||
};
|
};
|
||||||
|
|
||||||
#define BPF_REG_SIZE 8 /* size of eBPF register in bytes */
|
#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.
|
* default to pointer reference on zero initialization of a state.
|
||||||
*/
|
*/
|
||||||
enum ref_state_type {
|
enum ref_state_type {
|
||||||
REF_TYPE_PTR = 0,
|
REF_TYPE_PTR = 1,
|
||||||
REF_TYPE_LOCK,
|
REF_TYPE_IRQ = 2,
|
||||||
|
REF_TYPE_LOCK = 3,
|
||||||
} type;
|
} type;
|
||||||
/* Track each reference created with a unique id, even if the same
|
/* Track each reference created with a unique id, even if the same
|
||||||
* instruction creates the reference multiple times (eg, via CALL).
|
* instruction creates the reference multiple times (eg, via CALL).
|
||||||
@ -315,9 +317,6 @@ struct bpf_func_state {
|
|||||||
u32 callback_depth;
|
u32 callback_depth;
|
||||||
|
|
||||||
/* The following fields should be last. See copy_func_state() */
|
/* 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
|
/* The state of the stack. Each element of the array describes BPF_REG_SIZE
|
||||||
* (i.e. 8) bytes worth of stack memory.
|
* (i.e. 8) bytes worth of stack memory.
|
||||||
* stack[0] represents bytes [*(r10-8)..*(r10-1)]
|
* stack[0] represents bytes [*(r10-8)..*(r10-1)]
|
||||||
@ -370,6 +369,8 @@ struct bpf_verifier_state {
|
|||||||
/* call stack tracking */
|
/* call stack tracking */
|
||||||
struct bpf_func_state *frame[MAX_CALL_FRAMES];
|
struct bpf_func_state *frame[MAX_CALL_FRAMES];
|
||||||
struct bpf_verifier_state *parent;
|
struct bpf_verifier_state *parent;
|
||||||
|
/* Acquired reference states */
|
||||||
|
struct bpf_reference_state *refs;
|
||||||
/*
|
/*
|
||||||
* 'branches' field is the number of branches left to explore:
|
* 'branches' field is the number of branches left to explore:
|
||||||
* 0 - all possible paths from this state reached bpf_exit or
|
* 0 - all possible paths from this state reached bpf_exit or
|
||||||
@ -419,9 +420,13 @@ struct bpf_verifier_state {
|
|||||||
u32 insn_idx;
|
u32 insn_idx;
|
||||||
u32 curframe;
|
u32 curframe;
|
||||||
|
|
||||||
bool speculative;
|
u32 acquired_refs;
|
||||||
|
u32 active_locks;
|
||||||
|
u32 active_preempt_locks;
|
||||||
|
u32 active_irq_id;
|
||||||
bool active_rcu_lock;
|
bool active_rcu_lock;
|
||||||
u32 active_preempt_lock;
|
|
||||||
|
bool speculative;
|
||||||
/* If this state was ever pointed-to by other state's loop_entry field
|
/* 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
|
* this flag would be set to true. Used to avoid freeing such states
|
||||||
* while they are still in use.
|
* 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_type_str(const struct btf *btf, u32 btf_id);
|
||||||
const char *iter_state_str(enum bpf_iter_state state);
|
const char *iter_state_str(enum bpf_iter_state state);
|
||||||
|
|
||||||
void print_verifier_state(struct bpf_verifier_env *env,
|
void print_verifier_state(struct bpf_verifier_env *env, const struct bpf_verifier_state *vstate,
|
||||||
const struct bpf_func_state *state, bool print_all);
|
u32 frameno, bool print_all);
|
||||||
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);
|
||||||
|
|
||||||
#endif /* _LINUX_BPF_VERIFIER_H */
|
#endif /* _LINUX_BPF_VERIFIER_H */
|
||||||
|
@ -1573,6 +1573,16 @@ union bpf_attr {
|
|||||||
* If provided, prog_flags should have BPF_F_TOKEN_FD flag set.
|
* If provided, prog_flags should have BPF_F_TOKEN_FD flag set.
|
||||||
*/
|
*/
|
||||||
__s32 prog_token_fd;
|
__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 */
|
struct { /* anonymous struct used by BPF_OBJ_* commands */
|
||||||
|
@ -7887,13 +7887,8 @@ struct btf *btf_get_by_fd(int fd)
|
|||||||
struct btf *btf;
|
struct btf *btf;
|
||||||
CLASS(fd, f)(fd);
|
CLASS(fd, f)(fd);
|
||||||
|
|
||||||
if (fd_empty(f))
|
btf = __btf_get_by_fd(f);
|
||||||
return ERR_PTR(-EBADF);
|
if (!IS_ERR(btf))
|
||||||
|
|
||||||
if (fd_file(f)->f_op != &btf_fops)
|
|
||||||
return ERR_PTR(-EINVAL);
|
|
||||||
|
|
||||||
btf = fd_file(f)->private_data;
|
|
||||||
refcount_inc(&btf->refcnt);
|
refcount_inc(&btf->refcnt);
|
||||||
|
|
||||||
return btf;
|
return btf;
|
||||||
|
@ -3057,6 +3057,21 @@ __bpf_kfunc int bpf_copy_from_user_str(void *dst, u32 dst__sz, const void __user
|
|||||||
return ret + 1;
|
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();
|
__bpf_kfunc_end_defs();
|
||||||
|
|
||||||
BTF_KFUNCS_START(generic_btf_ids)
|
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_pid, KF_ACQUIRE | KF_RET_NULL)
|
||||||
BTF_ID_FLAGS(func, bpf_task_from_vpid, KF_ACQUIRE | KF_RET_NULL)
|
BTF_ID_FLAGS(func, bpf_task_from_vpid, KF_ACQUIRE | KF_RET_NULL)
|
||||||
BTF_ID_FLAGS(func, bpf_throw)
|
BTF_ID_FLAGS(func, bpf_throw)
|
||||||
|
#ifdef CONFIG_BPF_EVENTS
|
||||||
BTF_ID_FLAGS(func, bpf_send_signal_task, KF_TRUSTED_ARGS)
|
BTF_ID_FLAGS(func, bpf_send_signal_task, KF_TRUSTED_ARGS)
|
||||||
|
#endif
|
||||||
BTF_KFUNCS_END(generic_btf_ids)
|
BTF_KFUNCS_END(generic_btf_ids)
|
||||||
|
|
||||||
static const struct btf_kfunc_id_set generic_kfunc_set = {
|
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_is_rdonly)
|
||||||
BTF_ID_FLAGS(func, bpf_dynptr_size)
|
BTF_ID_FLAGS(func, bpf_dynptr_size)
|
||||||
BTF_ID_FLAGS(func, bpf_dynptr_clone)
|
BTF_ID_FLAGS(func, bpf_dynptr_clone)
|
||||||
|
#ifdef CONFIG_NET
|
||||||
BTF_ID_FLAGS(func, bpf_modify_return_test_tp)
|
BTF_ID_FLAGS(func, bpf_modify_return_test_tp)
|
||||||
|
#endif
|
||||||
BTF_ID_FLAGS(func, bpf_wq_init)
|
BTF_ID_FLAGS(func, bpf_wq_init)
|
||||||
BTF_ID_FLAGS(func, bpf_wq_set_callback_impl)
|
BTF_ID_FLAGS(func, bpf_wq_set_callback_impl)
|
||||||
BTF_ID_FLAGS(func, bpf_wq_start)
|
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_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_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_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)
|
BTF_KFUNCS_END(common_btf_ids)
|
||||||
|
|
||||||
static const struct btf_kfunc_id_set common_kfunc_set = {
|
static const struct btf_kfunc_id_set common_kfunc_set = {
|
||||||
|
@ -537,6 +537,7 @@ static char slot_type_char[] = {
|
|||||||
[STACK_ZERO] = '0',
|
[STACK_ZERO] = '0',
|
||||||
[STACK_DYNPTR] = 'd',
|
[STACK_DYNPTR] = 'd',
|
||||||
[STACK_ITER] = 'i',
|
[STACK_ITER] = 'i',
|
||||||
|
[STACK_IRQ_FLAG] = 'f'
|
||||||
};
|
};
|
||||||
|
|
||||||
static void print_liveness(struct bpf_verifier_env *env,
|
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, ")");
|
verbose(env, ")");
|
||||||
}
|
}
|
||||||
|
|
||||||
void print_verifier_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,
|
||||||
bool print_all)
|
u32 frameno, bool print_all)
|
||||||
{
|
{
|
||||||
|
const struct bpf_func_state *state = vstate->frame[frameno];
|
||||||
const struct bpf_reg_state *reg;
|
const struct bpf_reg_state *reg;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
@ -843,11 +845,11 @@ void print_verifier_state(struct bpf_verifier_env *env, const struct bpf_func_st
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (state->acquired_refs && state->refs[0].id) {
|
if (vstate->acquired_refs && vstate->refs[0].id) {
|
||||||
verbose(env, " refs=%d", state->refs[0].id);
|
verbose(env, " refs=%d", vstate->refs[0].id);
|
||||||
for (i = 1; i < state->acquired_refs; i++)
|
for (i = 1; i < vstate->acquired_refs; i++)
|
||||||
if (state->refs[i].id)
|
if (vstate->refs[i].id)
|
||||||
verbose(env, ",%d", state->refs[i].id);
|
verbose(env, ",%d", vstate->refs[i].id);
|
||||||
}
|
}
|
||||||
if (state->in_callback_fn)
|
if (state->in_callback_fn)
|
||||||
verbose(env, " cb");
|
verbose(env, " cb");
|
||||||
@ -864,7 +866,8 @@ static inline u32 vlog_alignment(u32 pos)
|
|||||||
BPF_LOG_MIN_ALIGNMENT) - pos - 1;
|
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) {
|
if (env->prev_log_pos && env->prev_log_pos == env->log.end_pos) {
|
||||||
/* remove new line character */
|
/* remove new line character */
|
||||||
@ -873,5 +876,5 @@ void print_insn_state(struct bpf_verifier_env *env, const struct bpf_func_state
|
|||||||
} else {
|
} else {
|
||||||
verbose(env, "%d:", env->insn_idx);
|
verbose(env, "%d:", env->insn_idx);
|
||||||
}
|
}
|
||||||
print_verifier_state(env, state, false);
|
print_verifier_state(env, vstate, frameno, false);
|
||||||
}
|
}
|
||||||
|
@ -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 */
|
/* 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)
|
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
@ -357,17 +357,6 @@ static const struct bpf_func_proto bpf_probe_write_user_proto = {
|
|||||||
.arg3_type = ARG_CONST_SIZE,
|
.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 MAX_TRACE_PRINTK_VARARGS 3
|
||||||
#define BPF_TRACE_PRINTK_SIZE 1024
|
#define BPF_TRACE_PRINTK_SIZE 1024
|
||||||
|
|
||||||
@ -1444,6 +1433,8 @@ late_initcall(bpf_key_sig_kfuncs_init);
|
|||||||
static const struct bpf_func_proto *
|
static const struct bpf_func_proto *
|
||||||
bpf_tracing_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
|
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) {
|
switch (func_id) {
|
||||||
case BPF_FUNC_map_lookup_elem:
|
case BPF_FUNC_map_lookup_elem:
|
||||||
return &bpf_map_lookup_elem_proto;
|
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;
|
return &bpf_perf_event_read_proto;
|
||||||
case BPF_FUNC_get_prandom_u32:
|
case BPF_FUNC_get_prandom_u32:
|
||||||
return &bpf_get_prandom_u32_proto;
|
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:
|
case BPF_FUNC_probe_read_user:
|
||||||
return &bpf_probe_read_user_proto;
|
return &bpf_probe_read_user_proto;
|
||||||
case BPF_FUNC_probe_read_kernel:
|
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:
|
case BPF_FUNC_trace_vprintk:
|
||||||
return bpf_get_trace_vprintk_proto();
|
return bpf_get_trace_vprintk_proto();
|
||||||
default:
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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_IN:
|
||||||
case BPF_PROG_TYPE_LWT_OUT:
|
case BPF_PROG_TYPE_LWT_OUT:
|
||||||
case BPF_PROG_TYPE_LWT_XMIT:
|
case BPF_PROG_TYPE_LWT_XMIT:
|
||||||
|
case BPF_PROG_TYPE_CGROUP_SKB:
|
||||||
is_direct_pkt_access = true;
|
is_direct_pkt_access = true;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -123,7 +123,7 @@ always-y += ibumad_kern.o
|
|||||||
always-y += hbm_out_kern.o
|
always-y += hbm_out_kern.o
|
||||||
always-y += hbm_edt_kern.o
|
always-y += hbm_edt_kern.o
|
||||||
|
|
||||||
TPROGS_CFLAGS = $(TPROGS_USER_CFLAGS)
|
COMMON_CFLAGS = $(TPROGS_USER_CFLAGS)
|
||||||
TPROGS_LDFLAGS = $(TPROGS_USER_LDFLAGS)
|
TPROGS_LDFLAGS = $(TPROGS_USER_LDFLAGS)
|
||||||
|
|
||||||
ifeq ($(ARCH), arm)
|
ifeq ($(ARCH), arm)
|
||||||
|
@ -63,7 +63,6 @@ SEC("tc_mark")
|
|||||||
int _tc_mark(struct __sk_buff *ctx)
|
int _tc_mark(struct __sk_buff *ctx)
|
||||||
{
|
{
|
||||||
void *data = (void *)(unsigned long)ctx->data;
|
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;
|
void *data_meta = (void *)(unsigned long)ctx->data_meta;
|
||||||
struct meta_info *meta = data_meta;
|
struct meta_info *meta = data_meta;
|
||||||
|
|
||||||
|
@ -283,7 +283,11 @@ vmlinux_link vmlinux
|
|||||||
# fill in BTF IDs
|
# fill in BTF IDs
|
||||||
if is_enabled CONFIG_DEBUG_INFO_BTF; then
|
if is_enabled CONFIG_DEBUG_INFO_BTF; then
|
||||||
info BTFIDS vmlinux
|
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
|
fi
|
||||||
|
|
||||||
mksysmap vmlinux System.map
|
mksysmap vmlinux System.map
|
||||||
|
@ -13,7 +13,6 @@ static struct security_hook_list bpf_lsm_hooks[] __ro_after_init = {
|
|||||||
#include <linux/lsm_hook_defs.h>
|
#include <linux/lsm_hook_defs.h>
|
||||||
#undef LSM_HOOK
|
#undef LSM_HOOK
|
||||||
LSM_HOOK_INIT(inode_free_security, bpf_inode_storage_free),
|
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 = {
|
static const struct lsm_id bpf_lsmid = {
|
||||||
|
@ -24,7 +24,7 @@ BTF COMMANDS
|
|||||||
=============
|
=============
|
||||||
|
|
||||||
| **bpftool** **btf** { **show** | **list** } [**id** *BTF_ID*]
|
| **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**
|
| **bpftool** **btf help**
|
||||||
|
|
|
|
||||||
| *BTF_SRC* := { **id** *BTF_ID* | **prog** *PROG* | **map** *MAP* [{**key** | **value** | **kv** | **all**}] | **file** *FILE* }
|
| *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
|
that hold open file descriptors (FDs) against BTF objects. On such kernels
|
||||||
bpftool will automatically emit this information as well.
|
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*.
|
Dump BTF entries from a given *BTF_SRC*.
|
||||||
|
|
||||||
When **id** is specified, BTF object with that ID will be loaded and all
|
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
|
formatting, the output is sorted by default. Use the **unsorted** option
|
||||||
to avoid sorting the output.
|
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
|
bpftool btf help
|
||||||
Print short help message.
|
Print short help message.
|
||||||
|
|
||||||
|
@ -930,19 +930,24 @@ _bpftool()
|
|||||||
format)
|
format)
|
||||||
COMPREPLY=( $( compgen -W "c raw" -- "$cur" ) )
|
COMPREPLY=( $( compgen -W "c raw" -- "$cur" ) )
|
||||||
;;
|
;;
|
||||||
|
root_id)
|
||||||
|
return 0;
|
||||||
|
;;
|
||||||
c)
|
c)
|
||||||
COMPREPLY=( $( compgen -W "unsorted" -- "$cur" ) )
|
COMPREPLY=( $( compgen -W "unsorted root_id" -- "$cur" ) )
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
# emit extra options
|
# emit extra options
|
||||||
case ${words[3]} in
|
case ${words[3]} in
|
||||||
id|file)
|
id|file)
|
||||||
|
COMPREPLY=( $( compgen -W "root_id" -- "$cur" ) )
|
||||||
_bpftool_once_attr 'format'
|
_bpftool_once_attr 'format'
|
||||||
;;
|
;;
|
||||||
map|prog)
|
map|prog)
|
||||||
if [[ ${words[3]} == "map" ]] && [[ $cword == 6 ]]; then
|
if [[ ${words[3]} == "map" ]] && [[ $cword == 6 ]]; then
|
||||||
COMPREPLY+=( $( compgen -W "key value kv all" -- "$cur" ) )
|
COMPREPLY+=( $( compgen -W "key value kv all" -- "$cur" ) )
|
||||||
fi
|
fi
|
||||||
|
COMPREPLY=( $( compgen -W "root_id" -- "$cur" ) )
|
||||||
_bpftool_once_attr 'format'
|
_bpftool_once_attr 'format'
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
|
@ -27,6 +27,8 @@
|
|||||||
#define KFUNC_DECL_TAG "bpf_kfunc"
|
#define KFUNC_DECL_TAG "bpf_kfunc"
|
||||||
#define FASTCALL_DECL_TAG "bpf_fastcall"
|
#define FASTCALL_DECL_TAG "bpf_fastcall"
|
||||||
|
|
||||||
|
#define MAX_ROOT_IDS 16
|
||||||
|
|
||||||
static const char * const btf_kind_str[NR_BTF_KINDS] = {
|
static const char * const btf_kind_str[NR_BTF_KINDS] = {
|
||||||
[BTF_KIND_UNKN] = "UNKNOWN",
|
[BTF_KIND_UNKN] = "UNKNOWN",
|
||||||
[BTF_KIND_INT] = "INT",
|
[BTF_KIND_INT] = "INT",
|
||||||
@ -880,12 +882,14 @@ static int do_dump(int argc, char **argv)
|
|||||||
{
|
{
|
||||||
bool dump_c = false, sort_dump_c = true;
|
bool dump_c = false, sort_dump_c = true;
|
||||||
struct btf *btf = NULL, *base = NULL;
|
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;
|
int root_type_cnt = 0;
|
||||||
__u32 btf_id = -1;
|
__u32 btf_id = -1;
|
||||||
const char *src;
|
const char *src;
|
||||||
int fd = -1;
|
int fd = -1;
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
int i;
|
||||||
|
|
||||||
if (!REQ_ARGS(2)) {
|
if (!REQ_ARGS(2)) {
|
||||||
usage();
|
usage();
|
||||||
@ -973,6 +977,8 @@ static int do_dump(int argc, char **argv)
|
|||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
have_id_filtering = !!root_type_cnt;
|
||||||
|
|
||||||
while (argc) {
|
while (argc) {
|
||||||
if (is_prefix(*argv, "format")) {
|
if (is_prefix(*argv, "format")) {
|
||||||
NEXT_ARG();
|
NEXT_ARG();
|
||||||
@ -992,6 +998,36 @@ static int do_dump(int argc, char **argv)
|
|||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
NEXT_ARG();
|
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")) {
|
} else if (is_prefix(*argv, "unsorted")) {
|
||||||
sort_dump_c = false;
|
sort_dump_c = false;
|
||||||
NEXT_ARG();
|
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 (dump_c) {
|
||||||
if (json_output) {
|
if (json_output) {
|
||||||
p_err("JSON output for C-syntax dump is not supported");
|
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,
|
fprintf(stderr,
|
||||||
"Usage: %1$s %2$s { show | list } [id BTF_ID]\n"
|
"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"
|
" %1$s %2$s help\n"
|
||||||
"\n"
|
"\n"
|
||||||
" BTF_SRC := { id BTF_ID | prog PROG | map MAP [{key | value | kv | all}] | file FILE }\n"
|
" BTF_SRC := { id BTF_ID | prog PROG | map MAP [{key | value | kv | all}] | file FILE }\n"
|
||||||
|
@ -885,6 +885,28 @@ probe_v3_isa_extension(const char *define_prefix, __u32 ifindex)
|
|||||||
"V3_ISA_EXTENSION");
|
"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
|
static void
|
||||||
section_system_config(enum probe_component target, const char *define_prefix)
|
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_bounded_loops(define_prefix, ifindex);
|
||||||
probe_v2_isa_extension(define_prefix, ifindex);
|
probe_v2_isa_extension(define_prefix, ifindex);
|
||||||
probe_v3_isa_extension(define_prefix, ifindex);
|
probe_v3_isa_extension(define_prefix, ifindex);
|
||||||
|
probe_v4_isa_extension(define_prefix, ifindex);
|
||||||
print_end_section();
|
print_end_section();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,6 +141,7 @@ struct object {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static int verbose;
|
static int verbose;
|
||||||
|
static int warnings;
|
||||||
|
|
||||||
static int eprintf(int level, int var, const char *fmt, ...)
|
static int eprintf(int level, int var, const char *fmt, ...)
|
||||||
{
|
{
|
||||||
@ -604,6 +605,7 @@ static int symbols_resolve(struct object *obj)
|
|||||||
if (id->id) {
|
if (id->id) {
|
||||||
pr_info("WARN: multiple IDs found for '%s': %d, %d - using %d\n",
|
pr_info("WARN: multiple IDs found for '%s': %d, %d - using %d\n",
|
||||||
str, id->id, type_id, id->id);
|
str, id->id, type_id, id->id);
|
||||||
|
warnings++;
|
||||||
} else {
|
} else {
|
||||||
id->id = type_id;
|
id->id = type_id;
|
||||||
(*nr)--;
|
(*nr)--;
|
||||||
@ -625,8 +627,10 @@ static int id_patch(struct object *obj, struct btf_id *id)
|
|||||||
int i;
|
int i;
|
||||||
|
|
||||||
/* For set, set8, id->id may be 0 */
|
/* 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);
|
pr_err("WARN: resolve_btfids: unresolved symbol %s\n", id->name);
|
||||||
|
warnings++;
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0; i < id->addr_cnt; i++) {
|
for (i = 0; i < id->addr_cnt; i++) {
|
||||||
unsigned long addr = id->addr[i];
|
unsigned long addr = id->addr[i];
|
||||||
@ -782,6 +786,7 @@ int main(int argc, const char **argv)
|
|||||||
.funcs = RB_ROOT,
|
.funcs = RB_ROOT,
|
||||||
.sets = RB_ROOT,
|
.sets = RB_ROOT,
|
||||||
};
|
};
|
||||||
|
bool fatal_warnings = false;
|
||||||
struct option btfid_options[] = {
|
struct option btfid_options[] = {
|
||||||
OPT_INCR('v', "verbose", &verbose,
|
OPT_INCR('v', "verbose", &verbose,
|
||||||
"be more verbose (show errors, etc)"),
|
"be more verbose (show errors, etc)"),
|
||||||
@ -789,6 +794,8 @@ int main(int argc, const char **argv)
|
|||||||
"BTF data"),
|
"BTF data"),
|
||||||
OPT_STRING('b', "btf_base", &obj.base_btf_path, "file",
|
OPT_STRING('b', "btf_base", &obj.base_btf_path, "file",
|
||||||
"path of file providing base BTF"),
|
"path of file providing base BTF"),
|
||||||
|
OPT_BOOLEAN(0, "fatal_warnings", &fatal_warnings,
|
||||||
|
"turn warnings into errors"),
|
||||||
OPT_END()
|
OPT_END()
|
||||||
};
|
};
|
||||||
int err = -1;
|
int err = -1;
|
||||||
@ -823,6 +830,7 @@ int main(int argc, const char **argv)
|
|||||||
if (symbols_patch(&obj))
|
if (symbols_patch(&obj))
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
if (!(fatal_warnings && warnings))
|
||||||
err = 0;
|
err = 0;
|
||||||
out:
|
out:
|
||||||
if (obj.efile.elf) {
|
if (obj.efile.elf) {
|
||||||
|
@ -273,6 +273,16 @@
|
|||||||
.off = OFF, \
|
.off = OFF, \
|
||||||
.imm = 0 })
|
.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 */
|
/* Function call */
|
||||||
|
|
||||||
#define BPF_EMIT_CALL(FUNC) \
|
#define BPF_EMIT_CALL(FUNC) \
|
||||||
|
@ -1573,6 +1573,16 @@ union bpf_attr {
|
|||||||
* If provided, prog_flags should have BPF_F_TOKEN_FD flag set.
|
* If provided, prog_flags should have BPF_F_TOKEN_FD flag set.
|
||||||
*/
|
*/
|
||||||
__s32 prog_token_fd;
|
__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 */
|
struct { /* anonymous struct used by BPF_OBJ_* commands */
|
||||||
|
@ -238,7 +238,7 @@ int bpf_prog_load(enum bpf_prog_type prog_type,
|
|||||||
const struct bpf_insn *insns, size_t insn_cnt,
|
const struct bpf_insn *insns, size_t insn_cnt,
|
||||||
struct bpf_prog_load_opts *opts)
|
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;
|
void *finfo = NULL, *linfo = NULL;
|
||||||
const char *func_info, *line_info;
|
const char *func_info, *line_info;
|
||||||
__u32 log_size, log_level, attach_prog_fd, attach_btf_obj_fd;
|
__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.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 = ptr_to_u64(OPTS_GET(opts, fd_array, NULL));
|
||||||
|
attr.fd_array_cnt = OPTS_GET(opts, fd_array_cnt, 0);
|
||||||
|
|
||||||
if (log_level) {
|
if (log_level) {
|
||||||
attr.log_buf = ptr_to_u64(log_buf);
|
attr.log_buf = ptr_to_u64(log_buf);
|
||||||
|
@ -107,9 +107,12 @@ struct bpf_prog_load_opts {
|
|||||||
*/
|
*/
|
||||||
__u32 log_true_size;
|
__u32 log_true_size;
|
||||||
__u32 token_fd;
|
__u32 token_fd;
|
||||||
|
|
||||||
|
/* if set, provides the length of fd_array */
|
||||||
|
__u32 fd_array_cnt;
|
||||||
size_t :0;
|
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,
|
LIBBPF_API int bpf_prog_load(enum bpf_prog_type prog_type,
|
||||||
const char *prog_name, const char *license,
|
const char *prog_name, const char *license,
|
||||||
|
@ -283,7 +283,7 @@ static int btf_parse_str_sec(struct btf *btf)
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
if (!btf->base_btf && start[0]) {
|
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 -EINVAL;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1796,9 +1796,14 @@ struct bpf_linker_file_opts {
|
|||||||
struct bpf_linker;
|
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(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,
|
LIBBPF_API int bpf_linker__add_file(struct bpf_linker *linker,
|
||||||
const char *filename,
|
const char *filename,
|
||||||
const struct bpf_linker_file_opts *opts);
|
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 int bpf_linker__finalize(struct bpf_linker *linker);
|
||||||
LIBBPF_API void bpf_linker__free(struct bpf_linker *linker);
|
LIBBPF_API void bpf_linker__free(struct bpf_linker *linker);
|
||||||
|
|
||||||
|
@ -432,4 +432,8 @@ LIBBPF_1.5.0 {
|
|||||||
} LIBBPF_1.4.0;
|
} LIBBPF_1.4.0;
|
||||||
|
|
||||||
LIBBPF_1.6.0 {
|
LIBBPF_1.6.0 {
|
||||||
|
global:
|
||||||
|
bpf_linker__add_buf;
|
||||||
|
bpf_linker__add_fd;
|
||||||
|
bpf_linker__new_fd;
|
||||||
} LIBBPF_1.5.0;
|
} LIBBPF_1.5.0;
|
||||||
|
@ -4,6 +4,10 @@
|
|||||||
*
|
*
|
||||||
* Copyright (c) 2021 Facebook
|
* Copyright (c) 2021 Facebook
|
||||||
*/
|
*/
|
||||||
|
#ifndef _GNU_SOURCE
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@ -16,6 +20,7 @@
|
|||||||
#include <elf.h>
|
#include <elf.h>
|
||||||
#include <libelf.h>
|
#include <libelf.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
#include "libbpf.h"
|
#include "libbpf.h"
|
||||||
#include "btf.h"
|
#include "btf.h"
|
||||||
#include "libbpf_internal.h"
|
#include "libbpf_internal.h"
|
||||||
@ -152,15 +157,19 @@ struct bpf_linker {
|
|||||||
/* global (including extern) ELF symbols */
|
/* global (including extern) ELF symbols */
|
||||||
int glob_sym_cnt;
|
int glob_sym_cnt;
|
||||||
struct glob_sym *glob_syms;
|
struct glob_sym *glob_syms;
|
||||||
|
|
||||||
|
bool fd_is_owned;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define pr_warn_elf(fmt, ...) \
|
#define pr_warn_elf(fmt, ...) \
|
||||||
libbpf_print(LIBBPF_WARN, "libbpf: " fmt ": %s\n", ##__VA_ARGS__, elf_errmsg(-1))
|
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,
|
static int bpf_linker_add_file(struct bpf_linker *linker, int fd,
|
||||||
const struct bpf_linker_file_opts *opts,
|
const char *filename);
|
||||||
|
|
||||||
|
static int linker_load_obj_file(struct bpf_linker *linker,
|
||||||
struct src_obj *obj);
|
struct src_obj *obj);
|
||||||
static int linker_sanity_check_elf(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);
|
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)
|
if (linker->elf)
|
||||||
elf_end(linker->elf);
|
elf_end(linker->elf);
|
||||||
|
|
||||||
if (linker->fd >= 0)
|
if (linker->fd >= 0 && linker->fd_is_owned)
|
||||||
close(linker->fd);
|
close(linker->fd);
|
||||||
|
|
||||||
strset__free(linker->strtab_strs);
|
strset__free(linker->strtab_strs);
|
||||||
@ -233,9 +242,63 @@ struct bpf_linker *bpf_linker__new(const char *filename, struct bpf_linker_opts
|
|||||||
if (!linker)
|
if (!linker)
|
||||||
return errno = ENOMEM, NULL;
|
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)
|
if (err)
|
||||||
goto err_out;
|
goto err_out;
|
||||||
|
|
||||||
@ -294,23 +357,12 @@ static Elf64_Sym *add_new_sym(struct bpf_linker *linker, size_t *sym_idx)
|
|||||||
return sym;
|
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;
|
int err, str_off;
|
||||||
Elf64_Sym *init_sym;
|
Elf64_Sym *init_sym;
|
||||||
struct dst_sec *sec;
|
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);
|
linker->elf = elf_begin(linker->fd, ELF_C_WRITE, NULL);
|
||||||
if (!linker->elf) {
|
if (!linker->elf) {
|
||||||
pr_warn_elf("failed to create ELF object");
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int bpf_linker__add_file(struct bpf_linker *linker, const char *filename,
|
static int bpf_linker_add_file(struct bpf_linker *linker, int fd,
|
||||||
const struct bpf_linker_file_opts *opts)
|
const char *filename)
|
||||||
{
|
{
|
||||||
struct src_obj obj = {};
|
struct src_obj obj = {};
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
if (!OPTS_VALID(opts, bpf_linker_file_opts))
|
obj.filename = filename;
|
||||||
return libbpf_err(-EINVAL);
|
obj.fd = fd;
|
||||||
|
|
||||||
if (!linker->elf)
|
err = err ?: linker_load_obj_file(linker, &obj);
|
||||||
return libbpf_err(-EINVAL);
|
|
||||||
|
|
||||||
err = err ?: linker_load_obj_file(linker, filename, opts, &obj);
|
|
||||||
err = err ?: linker_append_sec_data(linker, &obj);
|
err = err ?: linker_append_sec_data(linker, &obj);
|
||||||
err = err ?: linker_append_elf_syms(linker, &obj);
|
err = err ?: linker_append_elf_syms(linker, &obj);
|
||||||
err = err ?: linker_append_elf_relos(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);
|
free(obj.sym_map);
|
||||||
if (obj.elf)
|
if (obj.elf)
|
||||||
elf_end(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);
|
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)
|
static bool is_dwarf_sec_name(const char *name)
|
||||||
{
|
{
|
||||||
/* approximation, but the actual list is too long */
|
/* 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;
|
return sec;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int linker_load_obj_file(struct bpf_linker *linker, const char *filename,
|
static int linker_load_obj_file(struct bpf_linker *linker,
|
||||||
const struct bpf_linker_file_opts *opts,
|
|
||||||
struct src_obj *obj)
|
struct src_obj *obj)
|
||||||
{
|
{
|
||||||
int err = 0;
|
int err = 0;
|
||||||
@ -554,36 +681,26 @@ static int linker_load_obj_file(struct bpf_linker *linker, const char *filename,
|
|||||||
#error "Unknown __BYTE_ORDER__"
|
#error "Unknown __BYTE_ORDER__"
|
||||||
#endif
|
#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);
|
obj->elf = elf_begin(obj->fd, ELF_C_READ_MMAP, NULL);
|
||||||
if (!obj->elf) {
|
if (!obj->elf) {
|
||||||
err = -errno;
|
pr_warn_elf("failed to parse ELF file '%s'", obj->filename);
|
||||||
pr_warn_elf("failed to parse ELF file '%s'", filename);
|
return -EINVAL;
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Sanity check ELF file high-level properties */
|
/* Sanity check ELF file high-level properties */
|
||||||
ehdr = elf64_getehdr(obj->elf);
|
ehdr = elf64_getehdr(obj->elf);
|
||||||
if (!ehdr) {
|
if (!ehdr) {
|
||||||
err = -errno;
|
pr_warn_elf("failed to get ELF header for %s", obj->filename);
|
||||||
pr_warn_elf("failed to get ELF header for %s", filename);
|
return -EINVAL;
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Linker output endianness set by first input object */
|
/* Linker output endianness set by first input object */
|
||||||
obj_byteorder = ehdr->e_ident[EI_DATA];
|
obj_byteorder = ehdr->e_ident[EI_DATA];
|
||||||
if (obj_byteorder != ELFDATA2LSB && obj_byteorder != ELFDATA2MSB) {
|
if (obj_byteorder != ELFDATA2LSB && obj_byteorder != ELFDATA2MSB) {
|
||||||
err = -EOPNOTSUPP;
|
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;
|
return err;
|
||||||
}
|
}
|
||||||
if (link_byteorder == ELFDATANONE) {
|
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");
|
obj_byteorder == ELFDATA2MSB ? "big" : "little");
|
||||||
} else if (link_byteorder != obj_byteorder) {
|
} else if (link_byteorder != obj_byteorder) {
|
||||||
err = -EOPNOTSUPP;
|
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;
|
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_machine != EM_BPF
|
||||||
|| ehdr->e_ident[EI_CLASS] != ELFCLASS64) {
|
|| ehdr->e_ident[EI_CLASS] != ELFCLASS64) {
|
||||||
err = -EOPNOTSUPP;
|
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;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (elf_getshdrstrndx(obj->elf, &obj->shstrs_sec_idx)) {
|
if (elf_getshdrstrndx(obj->elf, &obj->shstrs_sec_idx)) {
|
||||||
err = -errno;
|
pr_warn_elf("failed to get SHSTRTAB section index for %s", obj->filename);
|
||||||
pr_warn_elf("failed to get SHSTRTAB section index for %s", filename);
|
return -EINVAL;
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
scn = NULL;
|
scn = NULL;
|
||||||
@ -618,26 +734,23 @@ static int linker_load_obj_file(struct bpf_linker *linker, const char *filename,
|
|||||||
|
|
||||||
shdr = elf64_getshdr(scn);
|
shdr = elf64_getshdr(scn);
|
||||||
if (!shdr) {
|
if (!shdr) {
|
||||||
err = -errno;
|
|
||||||
pr_warn_elf("failed to get section #%zu header for %s",
|
pr_warn_elf("failed to get section #%zu header for %s",
|
||||||
sec_idx, filename);
|
sec_idx, obj->filename);
|
||||||
return err;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
sec_name = elf_strptr(obj->elf, obj->shstrs_sec_idx, shdr->sh_name);
|
sec_name = elf_strptr(obj->elf, obj->shstrs_sec_idx, shdr->sh_name);
|
||||||
if (!sec_name) {
|
if (!sec_name) {
|
||||||
err = -errno;
|
|
||||||
pr_warn_elf("failed to get section #%zu name for %s",
|
pr_warn_elf("failed to get section #%zu name for %s",
|
||||||
sec_idx, filename);
|
sec_idx, obj->filename);
|
||||||
return err;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
data = elf_getdata(scn, 0);
|
data = elf_getdata(scn, 0);
|
||||||
if (!data) {
|
if (!data) {
|
||||||
err = -errno;
|
|
||||||
pr_warn_elf("failed to get section #%zu (%s) data from %s",
|
pr_warn_elf("failed to get section #%zu (%s) data from %s",
|
||||||
sec_idx, sec_name, filename);
|
sec_idx, sec_name, obj->filename);
|
||||||
return err;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
sec = add_src_sec(obj, sec_name);
|
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);
|
err = libbpf_get_error(obj->btf);
|
||||||
if (err) {
|
if (err) {
|
||||||
pr_warn("failed to parse .BTF from %s: %s\n",
|
pr_warn("failed to parse .BTF from %s: %s\n",
|
||||||
filename, errstr(err));
|
obj->filename, errstr(err));
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
sec->skipped = true;
|
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);
|
err = libbpf_get_error(obj->btf_ext);
|
||||||
if (err) {
|
if (err) {
|
||||||
pr_warn("failed to parse .BTF.ext from '%s': %s\n",
|
pr_warn("failed to parse .BTF.ext from '%s': %s\n",
|
||||||
filename, errstr(err));
|
obj->filename, errstr(err));
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
sec->skipped = true;
|
sec->skipped = true;
|
||||||
@ -700,7 +813,7 @@ static int linker_load_obj_file(struct bpf_linker *linker, const char *filename,
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
pr_warn("unrecognized section #%zu (%s) in %s\n",
|
pr_warn("unrecognized section #%zu (%s) in %s\n",
|
||||||
sec_idx, sec_name, filename);
|
sec_idx, sec_name, obj->filename);
|
||||||
err = -EINVAL;
|
err = -EINVAL;
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@ -2680,22 +2793,23 @@ int bpf_linker__finalize(struct bpf_linker *linker)
|
|||||||
|
|
||||||
/* Finalize ELF layout */
|
/* Finalize ELF layout */
|
||||||
if (elf_update(linker->elf, ELF_C_NULL) < 0) {
|
if (elf_update(linker->elf, ELF_C_NULL) < 0) {
|
||||||
err = -errno;
|
err = -EINVAL;
|
||||||
pr_warn_elf("failed to finalize ELF layout");
|
pr_warn_elf("failed to finalize ELF layout");
|
||||||
return libbpf_err(err);
|
return libbpf_err(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Write out final ELF contents */
|
/* Write out final ELF contents */
|
||||||
if (elf_update(linker->elf, ELF_C_WRITE) < 0) {
|
if (elf_update(linker->elf, ELF_C_WRITE) < 0) {
|
||||||
err = -errno;
|
err = -EINVAL;
|
||||||
pr_warn_elf("failed to write ELF contents");
|
pr_warn_elf("failed to write ELF contents");
|
||||||
return libbpf_err(err);
|
return libbpf_err(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
elf_end(linker->elf);
|
elf_end(linker->elf);
|
||||||
close(linker->fd);
|
|
||||||
|
|
||||||
linker->elf = NULL;
|
linker->elf = NULL;
|
||||||
|
|
||||||
|
if (linker->fd_is_owned)
|
||||||
|
close(linker->fd);
|
||||||
linker->fd = -1;
|
linker->fd = -1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -661,7 +661,7 @@ static int collect_usdt_targets(struct usdt_manager *man, Elf *elf, const char *
|
|||||||
* [0] https://sourceware.org/systemtap/wiki/UserSpaceProbeImplementation
|
* [0] https://sourceware.org/systemtap/wiki/UserSpaceProbeImplementation
|
||||||
*/
|
*/
|
||||||
usdt_abs_ip = note.loc_addr;
|
usdt_abs_ip = note.loc_addr;
|
||||||
if (base_addr)
|
if (base_addr && note.base_addr)
|
||||||
usdt_abs_ip += base_addr - note.base_addr;
|
usdt_abs_ip += base_addr - note.base_addr;
|
||||||
|
|
||||||
/* When attaching uprobes (which is what USDTs basically are)
|
/* When attaching uprobes (which is what USDTs basically are)
|
||||||
|
1
tools/testing/selftests/bpf/.gitignore
vendored
1
tools/testing/selftests/bpf/.gitignore
vendored
@ -18,7 +18,6 @@ feature
|
|||||||
urandom_read
|
urandom_read
|
||||||
test_sockmap
|
test_sockmap
|
||||||
test_lirc_mode2_user
|
test_lirc_mode2_user
|
||||||
test_flow_dissector
|
|
||||||
flow_dissector_load
|
flow_dissector_load
|
||||||
test_tcpnotify_user
|
test_tcpnotify_user
|
||||||
test_libbpf
|
test_libbpf
|
||||||
|
@ -129,11 +129,9 @@ TEST_FILES = xsk_prereqs.sh $(wildcard progs/btf_dump_test_case_*.c)
|
|||||||
TEST_PROGS := test_kmod.sh \
|
TEST_PROGS := test_kmod.sh \
|
||||||
test_xdp_redirect.sh \
|
test_xdp_redirect.sh \
|
||||||
test_xdp_redirect_multi.sh \
|
test_xdp_redirect_multi.sh \
|
||||||
test_xdp_meta.sh \
|
|
||||||
test_tunnel.sh \
|
test_tunnel.sh \
|
||||||
test_lwt_seg6local.sh \
|
test_lwt_seg6local.sh \
|
||||||
test_lirc_mode2.sh \
|
test_lirc_mode2.sh \
|
||||||
test_flow_dissector.sh \
|
|
||||||
test_xdp_vlan_mode_generic.sh \
|
test_xdp_vlan_mode_generic.sh \
|
||||||
test_xdp_vlan_mode_native.sh \
|
test_xdp_vlan_mode_native.sh \
|
||||||
test_lwt_ip_encap.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 \
|
with_tunnels.sh ima_setup.sh verify_sig_setup.sh \
|
||||||
test_xdp_vlan.sh test_bpftool.py
|
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'
|
# Compile but not part of 'make run_tests'
|
||||||
TEST_GEN_PROGS_EXTENDED = \
|
TEST_GEN_PROGS_EXTENDED = \
|
||||||
bench \
|
bench \
|
||||||
bpf_testmod.ko \
|
|
||||||
bpf_test_modorder_x.ko \
|
|
||||||
bpf_test_modorder_y.ko \
|
|
||||||
bpf_test_no_cfi.ko \
|
|
||||||
flow_dissector_load \
|
flow_dissector_load \
|
||||||
runqslower \
|
runqslower \
|
||||||
test_cpp \
|
test_cpp \
|
||||||
test_flow_dissector \
|
|
||||||
test_lirc_mode2_user \
|
test_lirc_mode2_user \
|
||||||
veristat \
|
veristat \
|
||||||
xdp_features \
|
xdp_features \
|
||||||
@ -184,8 +181,9 @@ override define CLEAN
|
|||||||
$(Q)$(RM) -r $(TEST_GEN_PROGS)
|
$(Q)$(RM) -r $(TEST_GEN_PROGS)
|
||||||
$(Q)$(RM) -r $(TEST_GEN_PROGS_EXTENDED)
|
$(Q)$(RM) -r $(TEST_GEN_PROGS_EXTENDED)
|
||||||
$(Q)$(RM) -r $(TEST_GEN_FILES)
|
$(Q)$(RM) -r $(TEST_GEN_FILES)
|
||||||
|
$(Q)$(RM) -r $(TEST_KMODS)
|
||||||
$(Q)$(RM) -r $(EXTRA_CLEAN)
|
$(Q)$(RM) -r $(EXTRA_CLEAN)
|
||||||
$(Q)$(MAKE) -C bpf_testmod clean
|
$(Q)$(MAKE) -C test_kmods clean
|
||||||
$(Q)$(MAKE) docs-clean
|
$(Q)$(MAKE) docs-clean
|
||||||
endef
|
endef
|
||||||
|
|
||||||
@ -251,7 +249,7 @@ endif
|
|||||||
# to build individual tests.
|
# to build individual tests.
|
||||||
# NOTE: Semicolon at the end is critical to override lib.mk's default static
|
# NOTE: Semicolon at the end is critical to override lib.mk's default static
|
||||||
# rule for binaries.
|
# rule for binaries.
|
||||||
$(notdir $(TEST_GEN_PROGS) \
|
$(notdir $(TEST_GEN_PROGS) $(TEST_KMODS) \
|
||||||
$(TEST_GEN_PROGS_EXTENDED)): %: $(OUTPUT)/% ;
|
$(TEST_GEN_PROGS_EXTENDED)): %: $(OUTPUT)/% ;
|
||||||
|
|
||||||
# sort removes libbpf duplicates when not cross-building
|
# sort removes libbpf duplicates when not cross-building
|
||||||
@ -305,37 +303,19 @@ $(OUTPUT)/sign-file: ../../../../scripts/sign-file.c
|
|||||||
$< -o $@ \
|
$< -o $@ \
|
||||||
$(shell $(PKG_CONFIG) --libs libcrypto 2> /dev/null || echo -lcrypto)
|
$(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])
|
# This should really be a grouped target, but make versions before 4.3 don't
|
||||||
$(call msg,MOD,,$@)
|
# support that for regular rules. However, pattern matching rules are implicitly
|
||||||
$(Q)$(RM) bpf_testmod/bpf_testmod.ko # force re-compilation
|
# treated as grouped even with older versions of make, so as a workaround, the
|
||||||
$(Q)$(MAKE) $(submake_extras) -C bpf_testmod \
|
# 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) \
|
RESOLVE_BTFIDS=$(RESOLVE_BTFIDS) \
|
||||||
EXTRA_CFLAGS='' EXTRA_LDFLAGS=''
|
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,,$@)
|
$(call msg,MOD,,$@)
|
||||||
$(Q)$(RM) bpf_test_no_cfi/bpf_test_no_cfi.ko # force re-compilation
|
$(Q)cp test_kmods/$(@F) $@
|
||||||
$(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 $@
|
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_BPFTOOL := $(HOST_SCRATCH_DIR)/sbin/bpftool
|
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
|
endef
|
||||||
|
|
||||||
# Determine target endianness.
|
# 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__')
|
grep 'define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__')
|
||||||
MENDIAN=$(if $(IS_LITTLE_ENDIAN),-mlittle-endian,-mbig-endian)
|
MENDIAN:=$(if $(IS_LITTLE_ENDIAN),-mlittle-endian,-mbig-endian)
|
||||||
BPF_TARGET_ENDIAN=$(if $(IS_LITTLE_ENDIAN),--target=bpfel,--target=bpfeb)
|
BPF_TARGET_ENDIAN:=$(if $(IS_LITTLE_ENDIAN),--target=bpfel,--target=bpfeb)
|
||||||
|
|
||||||
ifneq ($(CROSS_COMPILE),)
|
ifneq ($(CROSS_COMPILE),)
|
||||||
CLANG_TARGET_ARCH = --target=$(notdir $(CROSS_COMPILE:%-=%))
|
CLANG_TARGET_ARCH = --target=$(notdir $(CROSS_COMPILE:%-=%))
|
||||||
@ -760,14 +740,12 @@ TRUNNER_EXTRA_SOURCES := test_progs.c \
|
|||||||
json_writer.c \
|
json_writer.c \
|
||||||
flow_dissector_load.h \
|
flow_dissector_load.h \
|
||||||
ip_check_defrag_frags.h
|
ip_check_defrag_frags.h
|
||||||
TRUNNER_EXTRA_FILES := $(OUTPUT)/urandom_read $(OUTPUT)/bpf_testmod.ko \
|
TRUNNER_EXTRA_FILES := $(OUTPUT)/urandom_read \
|
||||||
$(OUTPUT)/bpf_test_no_cfi.ko \
|
|
||||||
$(OUTPUT)/bpf_test_modorder_x.ko \
|
|
||||||
$(OUTPUT)/bpf_test_modorder_y.ko \
|
|
||||||
$(OUTPUT)/liburandom_read.so \
|
$(OUTPUT)/liburandom_read.so \
|
||||||
$(OUTPUT)/xdp_synproxy \
|
$(OUTPUT)/xdp_synproxy \
|
||||||
$(OUTPUT)/sign-file \
|
$(OUTPUT)/sign-file \
|
||||||
$(OUTPUT)/uprobe_multi \
|
$(OUTPUT)/uprobe_multi \
|
||||||
|
$(TEST_KMOD_TARGETS) \
|
||||||
ima_setup.sh \
|
ima_setup.sh \
|
||||||
verify_sig_setup.sh \
|
verify_sig_setup.sh \
|
||||||
$(wildcard progs/btf_dump_test_case_*.c) \
|
$(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) \
|
EXTRA_CLEAN := $(SCRATCH_DIR) $(HOST_SCRATCH_DIR) \
|
||||||
prog_tests/tests.h map_tests/tests.h verifier/tests.h \
|
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 \
|
$(addprefix $(OUTPUT)/,*.o *.d *.skel.h *.lskel.h *.subskel.h \
|
||||||
no_alu32 cpuv4 bpf_gcc bpf_testmod.ko \
|
no_alu32 cpuv4 bpf_gcc \
|
||||||
bpf_test_no_cfi.ko \
|
|
||||||
bpf_test_modorder_x.ko \
|
|
||||||
bpf_test_modorder_y.ko \
|
|
||||||
liburandom_read.so) \
|
liburandom_read.so) \
|
||||||
$(OUTPUT)/FEATURE-DUMP.selftests
|
$(OUTPUT)/FEATURE-DUMP.selftests
|
||||||
|
|
||||||
|
@ -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
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -58,6 +58,7 @@ CONFIG_MPLS=y
|
|||||||
CONFIG_MPLS_IPTUNNEL=y
|
CONFIG_MPLS_IPTUNNEL=y
|
||||||
CONFIG_MPLS_ROUTING=y
|
CONFIG_MPLS_ROUTING=y
|
||||||
CONFIG_MPTCP=y
|
CONFIG_MPTCP=y
|
||||||
|
CONFIG_NET_ACT_GACT=y
|
||||||
CONFIG_NET_ACT_SKBMOD=y
|
CONFIG_NET_ACT_SKBMOD=y
|
||||||
CONFIG_NET_CLS=y
|
CONFIG_NET_CLS=y
|
||||||
CONFIG_NET_CLS_ACT=y
|
CONFIG_NET_CLS_ACT=y
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
#include <linux/limits.h>
|
#include <linux/limits.h>
|
||||||
|
|
||||||
#include <linux/ip.h>
|
#include <linux/ip.h>
|
||||||
#include <linux/udp.h>
|
#include <netinet/udp.h>
|
||||||
#include <netinet/tcp.h>
|
#include <netinet/tcp.h>
|
||||||
#include <net/if.h>
|
#include <net/if.h>
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@ typedef __u16 __sum16;
|
|||||||
#include <linux/sockios.h>
|
#include <linux/sockios.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <netinet/tcp.h>
|
#include <netinet/tcp.h>
|
||||||
|
#include <netinet/udp.h>
|
||||||
#include <bpf/bpf_endian.h>
|
#include <bpf/bpf_endian.h>
|
||||||
#include <net/if.h>
|
#include <net/if.h>
|
||||||
|
|
||||||
@ -105,6 +106,45 @@ static __u16 csum_fold(__u32 csum)
|
|||||||
return (__u16)~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,
|
static inline __sum16 csum_tcpudp_magic(__be32 saddr, __be32 daddr,
|
||||||
__u32 len, __u8 proto,
|
__u32 len, __u8 proto,
|
||||||
__wsum csum)
|
__wsum csum)
|
||||||
@ -120,6 +160,21 @@ static inline __sum16 csum_tcpudp_magic(__be32 saddr, __be32 daddr,
|
|||||||
return csum_fold((__u32)s);
|
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,
|
static inline __sum16 csum_ipv6_magic(const struct in6_addr *saddr,
|
||||||
const struct in6_addr *daddr,
|
const struct in6_addr *daddr,
|
||||||
__u32 len, __u8 proto,
|
__u32 len, __u8 proto,
|
||||||
@ -139,6 +194,47 @@ static inline __sum16 csum_ipv6_magic(const struct in6_addr *saddr,
|
|||||||
return csum_fold((__u32)s);
|
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;
|
struct tmonitor_ctx;
|
||||||
|
|
||||||
#ifdef TRAFFIC_MONITOR
|
#ifdef TRAFFIC_MONITOR
|
||||||
|
@ -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);
|
||||||
|
}
|
@ -2,7 +2,7 @@
|
|||||||
#define _GNU_SOURCE
|
#define _GNU_SOURCE
|
||||||
#include <test_progs.h>
|
#include <test_progs.h>
|
||||||
#include "progs/core_reloc_types.h"
|
#include "progs/core_reloc_types.h"
|
||||||
#include "bpf_testmod/bpf_testmod.h"
|
#include "test_kmods/bpf_testmod.h"
|
||||||
#include <linux/limits.h>
|
#include <linux/limits.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include <sys/syscall.h>
|
#include <sys/syscall.h>
|
||||||
|
441
tools/testing/selftests/bpf/prog_tests/fd_array.c
Normal file
441
tools/testing/selftests/bpf/prog_tests/fd_array.c
Normal 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();
|
||||||
|
}
|
@ -171,6 +171,10 @@ static void test_kprobe_fill_link_info(struct test_fill_link_info *skel,
|
|||||||
/* See also arch_adjust_kprobe_addr(). */
|
/* See also arch_adjust_kprobe_addr(). */
|
||||||
if (skel->kconfig->CONFIG_X86_KERNEL_IBT)
|
if (skel->kconfig->CONFIG_X86_KERNEL_IBT)
|
||||||
entry_offset = 4;
|
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);
|
err = verify_perf_link_info(link_fd, type, kprobe_addr, 0, entry_offset);
|
||||||
ASSERT_OK(err, "verify_perf_link_info");
|
ASSERT_OK(err, "verify_perf_link_info");
|
||||||
} else {
|
} else {
|
||||||
|
@ -7,39 +7,14 @@
|
|||||||
|
|
||||||
#include "bpf_flow.skel.h"
|
#include "bpf_flow.skel.h"
|
||||||
|
|
||||||
|
#define TEST_NS "flow_dissector_ns"
|
||||||
#define FLOW_CONTINUE_SADDR 0x7f00007f /* 127.0.0.127 */
|
#define FLOW_CONTINUE_SADDR 0x7f00007f /* 127.0.0.127 */
|
||||||
|
#define TEST_NAME_MAX_LEN 64
|
||||||
|
|
||||||
#ifndef IP_MF
|
#ifndef IP_MF
|
||||||
#define IP_MF 0x2000
|
#define IP_MF 0x2000
|
||||||
#endif
|
#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 ipv4_pkt {
|
||||||
struct ethhdr eth;
|
struct ethhdr eth;
|
||||||
struct iphdr iph;
|
struct iphdr iph;
|
||||||
@ -89,6 +64,19 @@ struct dvlan_ipv6_pkt {
|
|||||||
struct tcphdr tcp;
|
struct tcphdr tcp;
|
||||||
} __packed;
|
} __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 {
|
struct test {
|
||||||
const char *name;
|
const char *name;
|
||||||
union {
|
union {
|
||||||
@ -98,6 +86,7 @@ struct test {
|
|||||||
struct ipv6_pkt ipv6;
|
struct ipv6_pkt ipv6;
|
||||||
struct ipv6_frag_pkt ipv6_frag;
|
struct ipv6_frag_pkt ipv6_frag;
|
||||||
struct dvlan_ipv6_pkt dvlan_ipv6;
|
struct dvlan_ipv6_pkt dvlan_ipv6;
|
||||||
|
struct gre_minimal_pkt gre_minimal;
|
||||||
} pkt;
|
} pkt;
|
||||||
struct bpf_flow_keys keys;
|
struct bpf_flow_keys keys;
|
||||||
__u32 flags;
|
__u32 flags;
|
||||||
@ -106,7 +95,6 @@ struct test {
|
|||||||
|
|
||||||
#define VLAN_HLEN 4
|
#define VLAN_HLEN 4
|
||||||
|
|
||||||
static __u32 duration;
|
|
||||||
struct test tests[] = {
|
struct test tests[] = {
|
||||||
{
|
{
|
||||||
.name = "ipv4",
|
.name = "ipv4",
|
||||||
@ -444,8 +432,137 @@ struct test tests[] = {
|
|||||||
},
|
},
|
||||||
.retval = BPF_FLOW_DISSECTOR_CONTINUE,
|
.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)
|
static int create_tap(const char *ifname)
|
||||||
{
|
{
|
||||||
struct ifreq ifr = {
|
struct ifreq ifr = {
|
||||||
@ -533,22 +650,27 @@ static int init_prog_array(struct bpf_object *obj, struct bpf_map *prog_array)
|
|||||||
return 0;
|
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;
|
int i, err, keys_fd;
|
||||||
|
|
||||||
keys_fd = bpf_map__fd(keys);
|
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;
|
return;
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(tests); i++) {
|
for (i = 0; i < ARRAY_SIZE(tests); i++) {
|
||||||
/* Keep in sync with 'flags' from eth_get_headlen. */
|
/* Keep in sync with 'flags' from eth_get_headlen. */
|
||||||
__u32 eth_get_headlen_flags =
|
__u32 eth_get_headlen_flags =
|
||||||
BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG;
|
BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG;
|
||||||
LIBBPF_OPTS(bpf_test_run_opts, topts);
|
|
||||||
struct bpf_flow_keys flow_keys = {};
|
struct bpf_flow_keys flow_keys = {};
|
||||||
__u32 key = (__u32)(tests[i].keys.sport) << 16 |
|
__u32 key = (__u32)(tests[i].keys.sport) << 16 |
|
||||||
tests[i].keys.dport;
|
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
|
/* For skb-less case we can't pass input flags; run
|
||||||
* only the tests that have a matching set of flags.
|
* 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;
|
continue;
|
||||||
|
|
||||||
err = tx_tap(tap_fd, &tests[i].pkt, sizeof(tests[i].pkt));
|
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 */
|
/* check the stored flow_keys only if BPF_OK expected */
|
||||||
if (tests[i].retval != BPF_OK)
|
if (tests[i].retval != BPF_OK)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
err = bpf_map_lookup_elem(keys_fd, &key, &flow_keys);
|
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);
|
err = bpf_map_delete_elem(keys_fd, &key);
|
||||||
ASSERT_OK(err, "bpf_map_delete_elem");
|
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);
|
prog_fd = bpf_program__fd(skel->progs._dissect);
|
||||||
if (CHECK(prog_fd < 0, "bpf_program__fd", "err %d\n", prog_fd))
|
if (!ASSERT_OK_FD(prog_fd, "bpf_program__fd"))
|
||||||
return;
|
goto out_destroy_skel;
|
||||||
|
|
||||||
err = bpf_prog_attach(prog_fd, 0, BPF_FLOW_DISSECTOR, 0);
|
err = bpf_prog_attach(prog_fd, 0, BPF_FLOW_DISSECTOR, 0);
|
||||||
if (CHECK(err, "bpf_prog_attach", "err %d errno %d\n", err, errno))
|
if (!ASSERT_OK(err, "bpf_prog_attach"))
|
||||||
return;
|
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);
|
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;
|
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);
|
net_fd = open("/proc/self/ns/net", O_RDONLY);
|
||||||
if (CHECK(net_fd < 0, "open(/proc/self/ns/net)", "err %d\n", errno))
|
if (!ASSERT_OK_FD(net_fd, "open(/proc/self/ns/net"))
|
||||||
return;
|
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);
|
link = bpf_program__attach_netns(skel->progs._dissect, net_fd);
|
||||||
if (!ASSERT_OK_PTR(link, "attach_netns"))
|
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);
|
err = bpf_link__destroy(link);
|
||||||
CHECK(err, "bpf_link__destroy", "err %d\n", err);
|
ASSERT_OK(err, "bpf_link__destroy");
|
||||||
out_close:
|
|
||||||
|
out_close_tap:
|
||||||
|
close(tap_fd);
|
||||||
|
out_close_ns:
|
||||||
close(net_fd);
|
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;
|
struct bpf_flow *skel;
|
||||||
|
int i, err, prog_fd;
|
||||||
|
|
||||||
skel = bpf_flow__open_and_load();
|
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;
|
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);
|
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;
|
goto out_destroy_skel;
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(tests); i++) {
|
for (i = 0; i < ARRAY_SIZE(tests); i++) {
|
||||||
@ -641,6 +824,10 @@ void test_flow_dissector(void)
|
|||||||
);
|
);
|
||||||
static struct bpf_flow_keys ctx = {};
|
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) {
|
if (tests[i].flags) {
|
||||||
topts.ctx_in = &ctx;
|
topts.ctx_in = &ctx;
|
||||||
topts.ctx_size_in = sizeof(ctx);
|
topts.ctx_size_in = sizeof(ctx);
|
||||||
@ -656,26 +843,12 @@ void test_flow_dissector(void)
|
|||||||
continue;
|
continue;
|
||||||
ASSERT_EQ(topts.data_size_out, sizeof(flow_keys),
|
ASSERT_EQ(topts.data_size_out, sizeof(flow_keys),
|
||||||
"test_run data_size_out");
|
"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:
|
out_destroy_skel:
|
||||||
bpf_flow__destroy(skel);
|
bpf_flow__destroy(skel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
}
|
@ -2,7 +2,7 @@
|
|||||||
#include <test_progs.h>
|
#include <test_progs.h>
|
||||||
#include "cgroup_helpers.h"
|
#include "cgroup_helpers.h"
|
||||||
|
|
||||||
#include <linux/tcp.h>
|
#include <netinet/tcp.h>
|
||||||
#include <linux/netlink.h>
|
#include <linux/netlink.h>
|
||||||
#include "sockopt_sk.skel.h"
|
#include "sockopt_sk.skel.h"
|
||||||
|
|
||||||
|
@ -98,6 +98,7 @@
|
|||||||
#include "verifier_xdp_direct_packet_access.skel.h"
|
#include "verifier_xdp_direct_packet_access.skel.h"
|
||||||
#include "verifier_bits_iter.skel.h"
|
#include "verifier_bits_iter.skel.h"
|
||||||
#include "verifier_lsm.skel.h"
|
#include "verifier_lsm.skel.h"
|
||||||
|
#include "irq.skel.h"
|
||||||
|
|
||||||
#define MAX_ENTRIES 11
|
#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_xdp_direct_packet_access(void) { RUN(verifier_xdp_direct_packet_access); }
|
||||||
void test_verifier_bits_iter(void) { RUN(verifier_bits_iter); }
|
void test_verifier_bits_iter(void) { RUN(verifier_bits_iter); }
|
||||||
void test_verifier_lsm(void) { RUN(verifier_lsm); }
|
void test_verifier_lsm(void) { RUN(verifier_lsm); }
|
||||||
|
void test_irq(void) { RUN(irq); }
|
||||||
void test_verifier_mtu(void) { RUN(verifier_mtu); }
|
void test_verifier_mtu(void) { RUN(verifier_mtu); }
|
||||||
|
|
||||||
static int init_test_val_map(struct bpf_object *obj, char *map_name)
|
static int init_test_val_map(struct bpf_object *obj, char *map_name)
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
#include "network_helpers.h"
|
#include "network_helpers.h"
|
||||||
#include <linux/if_bonding.h>
|
#include <linux/if_bonding.h>
|
||||||
#include <linux/limits.h>
|
#include <linux/limits.h>
|
||||||
#include <linux/udp.h>
|
#include <netinet/udp.h>
|
||||||
#include <uapi/linux/netdev.h>
|
#include <uapi/linux/netdev.h>
|
||||||
|
|
||||||
#include "xdp_dummy.skel.h"
|
#include "xdp_dummy.skel.h"
|
||||||
|
@ -2,6 +2,14 @@
|
|||||||
#include <test_progs.h>
|
#include <test_progs.h>
|
||||||
#include <network_helpers.h>
|
#include <network_helpers.h>
|
||||||
#include "test_xdp_context_test_run.skel.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,
|
void test_xdp_context_error(int prog_fd, struct bpf_test_run_opts opts,
|
||||||
__u32 data_meta, __u32 data, __u32 data_end,
|
__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);
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
#include <linux/if_link.h>
|
#include <linux/if_link.h>
|
||||||
#include <linux/ipv6.h>
|
#include <linux/ipv6.h>
|
||||||
#include <linux/in6.h>
|
#include <linux/in6.h>
|
||||||
#include <linux/udp.h>
|
#include <netinet/udp.h>
|
||||||
#include <bpf/bpf_endian.h>
|
#include <bpf/bpf_endian.h>
|
||||||
#include <uapi/linux/netdev.h>
|
#include <uapi/linux/netdev.h>
|
||||||
#include "test_xdp_do_redirect.skel.h"
|
#include "test_xdp_do_redirect.skel.h"
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
#include <network_helpers.h>
|
#include <network_helpers.h>
|
||||||
#include <bpf/btf.h>
|
#include <bpf/btf.h>
|
||||||
#include <linux/if_link.h>
|
#include <linux/if_link.h>
|
||||||
#include <linux/udp.h>
|
#include <netinet/udp.h>
|
||||||
#include <net/if.h>
|
#include <net/if.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
#include <linux/errqueue.h>
|
#include <linux/errqueue.h>
|
||||||
#include <linux/if_link.h>
|
#include <linux/if_link.h>
|
||||||
#include <linux/net_tstamp.h>
|
#include <linux/net_tstamp.h>
|
||||||
#include <linux/udp.h>
|
#include <netinet/udp.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include <net/if.h>
|
#include <net/if.h>
|
||||||
#include <poll.h>
|
#include <poll.h>
|
||||||
@ -133,23 +133,6 @@ static void close_xsk(struct xsk *xsk)
|
|||||||
munmap(xsk->umem_area, UMEM_SIZE);
|
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)
|
static int generate_packet(struct xsk *xsk, __u16 dst_port)
|
||||||
{
|
{
|
||||||
struct xsk_tx_metadata *meta;
|
struct xsk_tx_metadata *meta;
|
||||||
@ -192,7 +175,7 @@ static int generate_packet(struct xsk *xsk, __u16 dst_port)
|
|||||||
iph->protocol = IPPROTO_UDP;
|
iph->protocol = IPPROTO_UDP;
|
||||||
ASSERT_EQ(inet_pton(FAMILY, TX_ADDR, &iph->saddr), 1, "inet_pton(TX_ADDR)");
|
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)");
|
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->source = htons(UDP_SOURCE_PORT);
|
||||||
udph->dest = htons(dst_port);
|
udph->dest = htons(dst_port);
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
#include <vmlinux.h>
|
#include <vmlinux.h>
|
||||||
#include <bpf/bpf_helpers.h>
|
#include <bpf/bpf_helpers.h>
|
||||||
#include <bpf/bpf_tracing.h>
|
#include <bpf/bpf_tracing.h>
|
||||||
#include "../bpf_testmod/bpf_testmod.h"
|
#include "../test_kmods/bpf_testmod.h"
|
||||||
|
|
||||||
char _license[] SEC("license") = "GPL";
|
char _license[] SEC("license") = "GPL";
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
#include <vmlinux.h>
|
#include <vmlinux.h>
|
||||||
#include <bpf/bpf_tracing.h>
|
#include <bpf/bpf_tracing.h>
|
||||||
#include <bpf/bpf_helpers.h>
|
#include <bpf/bpf_helpers.h>
|
||||||
#include "../bpf_testmod/bpf_testmod_kfunc.h"
|
#include "../test_kmods/bpf_testmod_kfunc.h"
|
||||||
|
|
||||||
struct map_value {
|
struct map_value {
|
||||||
struct prog_test_ref_kfunc __kptr *ptr;
|
struct prog_test_ref_kfunc __kptr *ptr;
|
||||||
|
@ -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";
|
@ -4,8 +4,8 @@
|
|||||||
#include <vmlinux.h>
|
#include <vmlinux.h>
|
||||||
#include <bpf/bpf_tracing.h>
|
#include <bpf/bpf_tracing.h>
|
||||||
#include "bpf_misc.h"
|
#include "bpf_misc.h"
|
||||||
#include "../bpf_testmod/bpf_testmod.h"
|
#include "../test_kmods/bpf_testmod.h"
|
||||||
#include "../bpf_testmod/bpf_testmod_kfunc.h"
|
#include "../test_kmods/bpf_testmod_kfunc.h"
|
||||||
|
|
||||||
char _license[] SEC("license") = "GPL";
|
char _license[] SEC("license") = "GPL";
|
||||||
|
|
||||||
|
@ -4,8 +4,8 @@
|
|||||||
#include <vmlinux.h>
|
#include <vmlinux.h>
|
||||||
#include <bpf/bpf_tracing.h>
|
#include <bpf/bpf_tracing.h>
|
||||||
#include "bpf_misc.h"
|
#include "bpf_misc.h"
|
||||||
#include "../bpf_testmod/bpf_testmod.h"
|
#include "../test_kmods/bpf_testmod.h"
|
||||||
#include "../bpf_testmod/bpf_testmod_kfunc.h"
|
#include "../test_kmods/bpf_testmod_kfunc.h"
|
||||||
|
|
||||||
char _license[] SEC("license") = "GPL";
|
char _license[] SEC("license") = "GPL";
|
||||||
|
|
||||||
|
@ -131,7 +131,7 @@ int reject_subprog_with_lock(void *ctx)
|
|||||||
}
|
}
|
||||||
|
|
||||||
SEC("?tc")
|
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)
|
int reject_with_rcu_read_lock(void *ctx)
|
||||||
{
|
{
|
||||||
bpf_rcu_read_lock();
|
bpf_rcu_read_lock();
|
||||||
@ -147,7 +147,7 @@ __noinline static int throwing_subprog(struct __sk_buff *ctx)
|
|||||||
}
|
}
|
||||||
|
|
||||||
SEC("?tc")
|
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)
|
int reject_subprog_with_rcu_read_lock(void *ctx)
|
||||||
{
|
{
|
||||||
bpf_rcu_read_lock();
|
bpf_rcu_read_lock();
|
||||||
|
444
tools/testing/selftests/bpf/progs/irq.c
Normal file
444
tools/testing/selftests/bpf/progs/irq.c
Normal 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";
|
@ -4,7 +4,7 @@
|
|||||||
#include "bpf_experimental.h"
|
#include "bpf_experimental.h"
|
||||||
#include <bpf/bpf_helpers.h>
|
#include <bpf/bpf_helpers.h>
|
||||||
#include "bpf_misc.h"
|
#include "bpf_misc.h"
|
||||||
#include "../bpf_testmod/bpf_testmod_kfunc.h"
|
#include "../test_kmods/bpf_testmod_kfunc.h"
|
||||||
|
|
||||||
char _license[] SEC("license") = "GPL";
|
char _license[] SEC("license") = "GPL";
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
#include <vmlinux.h>
|
#include <vmlinux.h>
|
||||||
#include <bpf/bpf_tracing.h>
|
#include <bpf/bpf_tracing.h>
|
||||||
#include <bpf/bpf_helpers.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;
|
static struct prog_test_ref_kfunc __kptr *v;
|
||||||
long total_sum = -1;
|
long total_sum = -1;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
#include <vmlinux.h>
|
#include <vmlinux.h>
|
||||||
#include <bpf/bpf_helpers.h>
|
#include <bpf/bpf_helpers.h>
|
||||||
#include "../bpf_testmod/bpf_testmod_kfunc.h"
|
#include "../test_kmods/bpf_testmod_kfunc.h"
|
||||||
|
|
||||||
SEC("tc")
|
SEC("tc")
|
||||||
int kfunc_destructive_test(void)
|
int kfunc_destructive_test(void)
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
/* Copyright (c) 2021 Facebook */
|
/* Copyright (c) 2021 Facebook */
|
||||||
#include <vmlinux.h>
|
#include <vmlinux.h>
|
||||||
#include <bpf/bpf_helpers.h>
|
#include <bpf/bpf_helpers.h>
|
||||||
#include "../bpf_testmod/bpf_testmod_kfunc.h"
|
#include "../test_kmods/bpf_testmod_kfunc.h"
|
||||||
|
|
||||||
struct syscall_test_args {
|
struct syscall_test_args {
|
||||||
__u8 data[16];
|
__u8 data[16];
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
#include <vmlinux.h>
|
#include <vmlinux.h>
|
||||||
#include <bpf/bpf_helpers.h>
|
#include <bpf/bpf_helpers.h>
|
||||||
#include "../bpf_testmod/bpf_testmod_kfunc.h"
|
#include "../test_kmods/bpf_testmod_kfunc.h"
|
||||||
|
|
||||||
SEC("tc")
|
SEC("tc")
|
||||||
int kfunc_call_fail(struct __sk_buff *ctx)
|
int kfunc_call_fail(struct __sk_buff *ctx)
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
/* Copyright (c) 2021 Facebook */
|
/* Copyright (c) 2021 Facebook */
|
||||||
#include <vmlinux.h>
|
#include <vmlinux.h>
|
||||||
#include <bpf/bpf_helpers.h>
|
#include <bpf/bpf_helpers.h>
|
||||||
#include "../bpf_testmod/bpf_testmod_kfunc.h"
|
#include "../test_kmods/bpf_testmod_kfunc.h"
|
||||||
|
|
||||||
SEC("tc")
|
SEC("tc")
|
||||||
int kfunc_call_test4(struct __sk_buff *skb)
|
int kfunc_call_test4(struct __sk_buff *skb)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
/* Copyright (c) 2021 Facebook */
|
/* 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;
|
extern const int bpf_prog_active __ksym;
|
||||||
int active_res = -1;
|
int active_res = -1;
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
#include <bpf/bpf_helpers.h>
|
#include <bpf/bpf_helpers.h>
|
||||||
#include <bpf/bpf_core_read.h>
|
#include <bpf/bpf_core_read.h>
|
||||||
#include "../bpf_experimental.h"
|
#include "../bpf_experimental.h"
|
||||||
#include "../bpf_testmod/bpf_testmod_kfunc.h"
|
#include "../test_kmods/bpf_testmod_kfunc.h"
|
||||||
|
|
||||||
struct plain_local;
|
struct plain_local;
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
#include <vmlinux.h>
|
#include <vmlinux.h>
|
||||||
#include <bpf/bpf_tracing.h>
|
#include <bpf/bpf_tracing.h>
|
||||||
#include <bpf/bpf_helpers.h>
|
#include <bpf/bpf_helpers.h>
|
||||||
#include "../bpf_testmod/bpf_testmod_kfunc.h"
|
#include "../test_kmods/bpf_testmod_kfunc.h"
|
||||||
|
|
||||||
struct map_value {
|
struct map_value {
|
||||||
struct prog_test_ref_kfunc __kptr_untrusted *unref_ptr;
|
struct prog_test_ref_kfunc __kptr_untrusted *unref_ptr;
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
#include <bpf/bpf_helpers.h>
|
#include <bpf/bpf_helpers.h>
|
||||||
#include <bpf/bpf_core_read.h>
|
#include <bpf/bpf_core_read.h>
|
||||||
#include "bpf_misc.h"
|
#include "bpf_misc.h"
|
||||||
#include "../bpf_testmod/bpf_testmod_kfunc.h"
|
#include "../test_kmods/bpf_testmod_kfunc.h"
|
||||||
|
|
||||||
struct map_value {
|
struct map_value {
|
||||||
char buf[8];
|
char buf[8];
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
#include "vmlinux.h"
|
#include "vmlinux.h"
|
||||||
#include <bpf/bpf_helpers.h>
|
#include <bpf/bpf_helpers.h>
|
||||||
#include <bpf/bpf_tracing.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";
|
char _license[] SEC("license") = "GPL";
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
#include "vmlinux.h"
|
#include "vmlinux.h"
|
||||||
#include <bpf/bpf_helpers.h>
|
#include <bpf/bpf_helpers.h>
|
||||||
#include <bpf/bpf_tracing.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";
|
char _license[] SEC("license") = "GPL";
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
#include <bpf/bpf_tracing.h>
|
#include <bpf/bpf_tracing.h>
|
||||||
#include <bpf/bpf_helpers.h>
|
#include <bpf/bpf_helpers.h>
|
||||||
#include "bpf_misc.h"
|
#include "bpf_misc.h"
|
||||||
#include "../bpf_testmod/bpf_testmod_kfunc.h"
|
#include "../test_kmods/bpf_testmod_kfunc.h"
|
||||||
|
|
||||||
char _license[] SEC("license") = "GPL";
|
char _license[] SEC("license") = "GPL";
|
||||||
|
|
||||||
|
@ -5,8 +5,10 @@
|
|||||||
#include "bpf_misc.h"
|
#include "bpf_misc.h"
|
||||||
#include "bpf_experimental.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")
|
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)
|
int preempt_lock_missing_1(struct __sk_buff *ctx)
|
||||||
{
|
{
|
||||||
bpf_preempt_disable();
|
bpf_preempt_disable();
|
||||||
@ -14,7 +16,7 @@ int preempt_lock_missing_1(struct __sk_buff *ctx)
|
|||||||
}
|
}
|
||||||
|
|
||||||
SEC("?tc")
|
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)
|
int preempt_lock_missing_2(struct __sk_buff *ctx)
|
||||||
{
|
{
|
||||||
bpf_preempt_disable();
|
bpf_preempt_disable();
|
||||||
@ -23,7 +25,7 @@ int preempt_lock_missing_2(struct __sk_buff *ctx)
|
|||||||
}
|
}
|
||||||
|
|
||||||
SEC("?tc")
|
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)
|
int preempt_lock_missing_3(struct __sk_buff *ctx)
|
||||||
{
|
{
|
||||||
bpf_preempt_disable();
|
bpf_preempt_disable();
|
||||||
@ -33,7 +35,7 @@ int preempt_lock_missing_3(struct __sk_buff *ctx)
|
|||||||
}
|
}
|
||||||
|
|
||||||
SEC("?tc")
|
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)
|
int preempt_lock_missing_3_minus_2(struct __sk_buff *ctx)
|
||||||
{
|
{
|
||||||
bpf_preempt_disable();
|
bpf_preempt_disable();
|
||||||
@ -55,7 +57,7 @@ static __noinline void preempt_enable(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
SEC("?tc")
|
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)
|
int preempt_lock_missing_1_subprog(struct __sk_buff *ctx)
|
||||||
{
|
{
|
||||||
preempt_disable();
|
preempt_disable();
|
||||||
@ -63,7 +65,7 @@ int preempt_lock_missing_1_subprog(struct __sk_buff *ctx)
|
|||||||
}
|
}
|
||||||
|
|
||||||
SEC("?tc")
|
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)
|
int preempt_lock_missing_2_subprog(struct __sk_buff *ctx)
|
||||||
{
|
{
|
||||||
preempt_disable();
|
preempt_disable();
|
||||||
@ -72,7 +74,7 @@ int preempt_lock_missing_2_subprog(struct __sk_buff *ctx)
|
|||||||
}
|
}
|
||||||
|
|
||||||
SEC("?tc")
|
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)
|
int preempt_lock_missing_2_minus_1_subprog(struct __sk_buff *ctx)
|
||||||
{
|
{
|
||||||
preempt_disable();
|
preempt_disable();
|
||||||
@ -113,6 +115,18 @@ int preempt_sleepable_helper(void *ctx)
|
|||||||
return 0;
|
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)
|
int __noinline preempt_global_subprog(void)
|
||||||
{
|
{
|
||||||
preempt_balance_subprog();
|
preempt_balance_subprog();
|
||||||
|
@ -4,8 +4,8 @@
|
|||||||
#include <vmlinux.h>
|
#include <vmlinux.h>
|
||||||
#include <bpf/bpf_tracing.h>
|
#include <bpf/bpf_tracing.h>
|
||||||
#include "bpf_misc.h"
|
#include "bpf_misc.h"
|
||||||
#include "../bpf_testmod/bpf_testmod.h"
|
#include "../test_kmods/bpf_testmod.h"
|
||||||
#include "../bpf_testmod/bpf_testmod_kfunc.h"
|
#include "../test_kmods/bpf_testmod_kfunc.h"
|
||||||
|
|
||||||
char _license[] SEC("license") = "GPL";
|
char _license[] SEC("license") = "GPL";
|
||||||
|
|
||||||
|
@ -4,8 +4,8 @@
|
|||||||
#include <vmlinux.h>
|
#include <vmlinux.h>
|
||||||
#include <bpf/bpf_tracing.h>
|
#include <bpf/bpf_tracing.h>
|
||||||
#include "bpf_misc.h"
|
#include "bpf_misc.h"
|
||||||
#include "../bpf_testmod/bpf_testmod.h"
|
#include "../test_kmods/bpf_testmod.h"
|
||||||
#include "../bpf_testmod/bpf_testmod_kfunc.h"
|
#include "../test_kmods/bpf_testmod_kfunc.h"
|
||||||
|
|
||||||
char _license[] SEC("license") = "GPL";
|
char _license[] SEC("license") = "GPL";
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
/* Copyright (c) 2024 Google LLC */
|
/* Copyright (c) 2024 Google LLC */
|
||||||
#include <vmlinux.h>
|
#include <vmlinux.h>
|
||||||
#include <bpf/bpf_helpers.h>
|
#include <bpf/bpf_helpers.h>
|
||||||
#include "../bpf_testmod/bpf_testmod_kfunc.h"
|
#include "../test_kmods/bpf_testmod_kfunc.h"
|
||||||
|
|
||||||
SEC("syscall")
|
SEC("syscall")
|
||||||
int init_sock(struct init_sock_args *args)
|
int init_sock(struct init_sock_args *args)
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
/* Copyright (c) 2024 Meta Platforms, Inc. and affiliates. */
|
/* Copyright (c) 2024 Meta Platforms, Inc. and affiliates. */
|
||||||
#include <vmlinux.h>
|
#include <vmlinux.h>
|
||||||
#include <bpf/bpf_helpers.h>
|
#include <bpf/bpf_helpers.h>
|
||||||
#include "../bpf_testmod/bpf_testmod.h"
|
#include "../test_kmods/bpf_testmod.h"
|
||||||
|
|
||||||
char _license[] SEC("license") = "GPL";
|
char _license[] SEC("license") = "GPL";
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
/* Copyright (c) 2024 Meta Platforms, Inc. and affiliates. */
|
/* Copyright (c) 2024 Meta Platforms, Inc. and affiliates. */
|
||||||
#include <vmlinux.h>
|
#include <vmlinux.h>
|
||||||
#include <bpf/bpf_tracing.h>
|
#include <bpf/bpf_tracing.h>
|
||||||
#include "../bpf_testmod/bpf_testmod.h"
|
#include "../test_kmods/bpf_testmod.h"
|
||||||
|
|
||||||
char _license[] SEC("license") = "GPL";
|
char _license[] SEC("license") = "GPL";
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
/* Copyright (c) 2024 Meta Platforms, Inc. and affiliates. */
|
/* Copyright (c) 2024 Meta Platforms, Inc. and affiliates. */
|
||||||
#include <vmlinux.h>
|
#include <vmlinux.h>
|
||||||
#include <bpf/bpf_tracing.h>
|
#include <bpf/bpf_tracing.h>
|
||||||
#include "../bpf_testmod/bpf_testmod.h"
|
#include "../test_kmods/bpf_testmod.h"
|
||||||
|
|
||||||
char _license[] SEC("license") = "GPL";
|
char _license[] SEC("license") = "GPL";
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
/* Copyright (c) 2024 Meta Platforms, Inc. and affiliates. */
|
/* Copyright (c) 2024 Meta Platforms, Inc. and affiliates. */
|
||||||
#include <vmlinux.h>
|
#include <vmlinux.h>
|
||||||
#include <bpf/bpf_tracing.h>
|
#include <bpf/bpf_tracing.h>
|
||||||
#include "../bpf_testmod/bpf_testmod.h"
|
#include "../test_kmods/bpf_testmod.h"
|
||||||
|
|
||||||
char _license[] SEC("license") = "GPL";
|
char _license[] SEC("license") = "GPL";
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
#include <vmlinux.h>
|
#include <vmlinux.h>
|
||||||
#include <bpf/bpf_helpers.h>
|
#include <bpf/bpf_helpers.h>
|
||||||
#include <bpf/bpf_tracing.h>
|
#include <bpf/bpf_tracing.h>
|
||||||
#include "../bpf_testmod/bpf_testmod.h"
|
#include "../test_kmods/bpf_testmod.h"
|
||||||
|
|
||||||
char _license[] SEC("license") = "GPL";
|
char _license[] SEC("license") = "GPL";
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
#include <vmlinux.h>
|
#include <vmlinux.h>
|
||||||
#include <bpf/bpf_helpers.h>
|
#include <bpf/bpf_helpers.h>
|
||||||
#include <bpf/bpf_tracing.h>
|
#include <bpf/bpf_tracing.h>
|
||||||
#include "../bpf_testmod/bpf_testmod.h"
|
#include "../test_kmods/bpf_testmod.h"
|
||||||
|
|
||||||
char _license[] SEC("license") = "GPL";
|
char _license[] SEC("license") = "GPL";
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
/* Copyright (c) 2024 Meta Platforms, Inc. and affiliates. */
|
/* Copyright (c) 2024 Meta Platforms, Inc. and affiliates. */
|
||||||
#include <vmlinux.h>
|
#include <vmlinux.h>
|
||||||
#include <bpf/bpf_tracing.h>
|
#include <bpf/bpf_tracing.h>
|
||||||
#include "../bpf_testmod/bpf_testmod.h"
|
#include "../test_kmods/bpf_testmod.h"
|
||||||
|
|
||||||
char _license[] SEC("license") = "GPL";
|
char _license[] SEC("license") = "GPL";
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
#include <vmlinux.h>
|
#include <vmlinux.h>
|
||||||
#include <bpf/bpf_helpers.h>
|
#include <bpf/bpf_helpers.h>
|
||||||
#include <bpf/bpf_tracing.h>
|
#include <bpf/bpf_tracing.h>
|
||||||
#include "../bpf_testmod/bpf_testmod.h"
|
#include "../test_kmods/bpf_testmod.h"
|
||||||
|
|
||||||
char _license[] SEC("license") = "GPL";
|
char _license[] SEC("license") = "GPL";
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
#include <vmlinux.h>
|
#include <vmlinux.h>
|
||||||
#include <bpf/bpf_helpers.h>
|
#include <bpf/bpf_helpers.h>
|
||||||
#include <bpf/bpf_tracing.h>
|
#include <bpf/bpf_tracing.h>
|
||||||
#include "../bpf_testmod/bpf_testmod.h"
|
#include "../test_kmods/bpf_testmod.h"
|
||||||
|
|
||||||
char _license[] SEC("license") = "GPL";
|
char _license[] SEC("license") = "GPL";
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
#include <vmlinux.h>
|
#include <vmlinux.h>
|
||||||
#include <bpf/bpf_helpers.h>
|
#include <bpf/bpf_helpers.h>
|
||||||
#include <bpf/bpf_tracing.h>
|
#include <bpf/bpf_tracing.h>
|
||||||
#include "../bpf_testmod/bpf_testmod.h"
|
#include "../test_kmods/bpf_testmod.h"
|
||||||
|
|
||||||
char _license[] SEC("license") = "GPL";
|
char _license[] SEC("license") = "GPL";
|
||||||
|
|
||||||
|
@ -76,9 +76,9 @@ static int btf_load(void)
|
|||||||
.magic = BTF_MAGIC,
|
.magic = BTF_MAGIC,
|
||||||
.version = BTF_VERSION,
|
.version = BTF_VERSION,
|
||||||
.hdr_len = sizeof(struct btf_header),
|
.hdr_len = sizeof(struct btf_header),
|
||||||
.type_len = sizeof(__u32) * 8,
|
.type_len = sizeof(raw_btf.types),
|
||||||
.str_off = sizeof(__u32) * 8,
|
.str_off = offsetof(struct btf_blob, str) - offsetof(struct btf_blob, types),
|
||||||
.str_len = sizeof(__u32),
|
.str_len = sizeof(raw_btf.str),
|
||||||
},
|
},
|
||||||
.types = {
|
.types = {
|
||||||
/* long */
|
/* long */
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
#include <linux/ipv6.h>
|
#include <linux/ipv6.h>
|
||||||
#include <linux/pkt_cls.h>
|
#include <linux/pkt_cls.h>
|
||||||
#include <linux/tcp.h>
|
#include <linux/tcp.h>
|
||||||
#include <linux/udp.h>
|
#include <netinet/udp.h>
|
||||||
|
|
||||||
#include <bpf/bpf_helpers.h>
|
#include <bpf/bpf_helpers.h>
|
||||||
#include <bpf/bpf_endian.h>
|
#include <bpf/bpf_endian.h>
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
#include <linux/in.h>
|
#include <linux/in.h>
|
||||||
#include <linux/ip.h>
|
#include <linux/ip.h>
|
||||||
#include <linux/ipv6.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
|
/* offsetof() is used in static asserts, and the libbpf-redefined CO-RE
|
||||||
* friendly version breaks compilation for older clang versions <= 15
|
* friendly version breaks compilation for older clang versions <= 15
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
#include <linux/ipv6.h>
|
#include <linux/ipv6.h>
|
||||||
#include <linux/pkt_cls.h>
|
#include <linux/pkt_cls.h>
|
||||||
#include <linux/tcp.h>
|
#include <linux/tcp.h>
|
||||||
#include <linux/udp.h>
|
#include <netinet/udp.h>
|
||||||
|
|
||||||
#include <bpf/bpf_helpers.h>
|
#include <bpf/bpf_helpers.h>
|
||||||
#include <bpf/bpf_endian.h>
|
#include <bpf/bpf_endian.h>
|
||||||
|
@ -6,13 +6,20 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
extern bool CONFIG_X86_KERNEL_IBT __kconfig __weak;
|
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
|
/* This function is here to have CONFIG_X86_KERNEL_IBT,
|
||||||
* used and added to object BTF.
|
* CONFIG_PPC_FTRACE_OUT_OF_LINE, CONFIG_KPROBES_ON_FTRACE,
|
||||||
|
* CONFIG_PPC6 used and added to object BTF.
|
||||||
*/
|
*/
|
||||||
int unused(void)
|
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")
|
SEC("kprobe")
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
#include <bpf/bpf_helpers.h>
|
#include <bpf/bpf_helpers.h>
|
||||||
#include "bpf_misc.h"
|
#include "bpf_misc.h"
|
||||||
#include "bpf_kfuncs.h"
|
#include "bpf_kfuncs.h"
|
||||||
#include "../bpf_testmod/bpf_testmod_kfunc.h"
|
#include "../test_kmods/bpf_testmod_kfunc.h"
|
||||||
|
|
||||||
SEC("tc")
|
SEC("tc")
|
||||||
int kfunc_dynptr_nullable_test1(struct __sk_buff *skb)
|
int kfunc_dynptr_nullable_test1(struct __sk_buff *skb)
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
#include <bpf/bpf_helpers.h>
|
#include <bpf/bpf_helpers.h>
|
||||||
#include <bpf/bpf_tracing.h>
|
#include <bpf/bpf_tracing.h>
|
||||||
#include <bpf/bpf_core_read.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;
|
__u32 raw_tp_read_sz = 0;
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
#include "vmlinux.h"
|
#include "vmlinux.h"
|
||||||
#include <bpf/bpf_helpers.h>
|
#include <bpf/bpf_helpers.h>
|
||||||
#include <bpf/bpf_tracing.h>
|
#include <bpf/bpf_tracing.h>
|
||||||
#include "../bpf_testmod/bpf_testmod.h"
|
#include "../test_kmods/bpf_testmod.h"
|
||||||
#include "bpf_misc.h"
|
#include "bpf_misc.h"
|
||||||
|
|
||||||
SEC("tp_btf/bpf_testmod_test_nullable_bare")
|
SEC("tp_btf/bpf_testmod_test_nullable_bare")
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
#define round_up(x, y) ((((x) - 1) | __round_mask(x, y)) + 1)
|
#define round_up(x, y) ((((x) - 1) | __round_mask(x, y)) + 1)
|
||||||
#define ctx_ptr(ctx, mem) (void *)(unsigned long)ctx->mem
|
#define ctx_ptr(ctx, mem) (void *)(unsigned long)ctx->mem
|
||||||
|
|
||||||
SEC("t")
|
SEC("tc")
|
||||||
int ing_cls(struct __sk_buff *ctx)
|
int ing_cls(struct __sk_buff *ctx)
|
||||||
{
|
{
|
||||||
__u8 *data, *data_meta, *data_end;
|
__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;
|
return diff ? TC_ACT_SHOT : TC_ACT_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
SEC("x")
|
SEC("xdp")
|
||||||
int ing_xdp(struct xdp_md *ctx)
|
int ing_xdp(struct xdp_md *ctx)
|
||||||
{
|
{
|
||||||
__u8 *data, *data_meta, *data_end;
|
__u8 *data, *data_meta, *data_end;
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
#include <vmlinux.h>
|
#include <vmlinux.h>
|
||||||
#include <bpf/bpf_tracing.h>
|
#include <bpf/bpf_tracing.h>
|
||||||
#include "bpf_misc.h"
|
#include "bpf_misc.h"
|
||||||
#include "../bpf_testmod/bpf_testmod.h"
|
#include "../test_kmods/bpf_testmod.h"
|
||||||
|
|
||||||
char _license[] SEC("license") = "GPL";
|
char _license[] SEC("license") = "GPL";
|
||||||
|
|
||||||
|
@ -187,7 +187,7 @@ l0_%=: r6 = r0; \
|
|||||||
|
|
||||||
SEC("cgroup/skb")
|
SEC("cgroup/skb")
|
||||||
__description("spin_lock: test6 missing unlock")
|
__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("")
|
__failure_unpriv __msg_unpriv("")
|
||||||
__naked void spin_lock_test6_missing_unlock(void)
|
__naked void spin_lock_test6_missing_unlock(void)
|
||||||
{
|
{
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
#include "bpf_experimental.h"
|
#include "bpf_experimental.h"
|
||||||
#include <bpf/bpf_helpers.h>
|
#include <bpf/bpf_helpers.h>
|
||||||
#include "bpf_misc.h"
|
#include "bpf_misc.h"
|
||||||
#include "../bpf_testmod/bpf_testmod_kfunc.h"
|
#include "../test_kmods/bpf_testmod_kfunc.h"
|
||||||
|
|
||||||
char _license[] SEC("license") = "GPL";
|
char _license[] SEC("license") = "GPL";
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
#include "bpf_experimental.h"
|
#include "bpf_experimental.h"
|
||||||
#include <bpf/bpf_helpers.h>
|
#include <bpf/bpf_helpers.h>
|
||||||
#include "bpf_misc.h"
|
#include "bpf_misc.h"
|
||||||
#include "../bpf_testmod/bpf_testmod_kfunc.h"
|
#include "../test_kmods/bpf_testmod_kfunc.h"
|
||||||
|
|
||||||
char _license[] SEC("license") = "GPL";
|
char _license[] SEC("license") = "GPL";
|
||||||
|
|
||||||
|
@ -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();
|
|
||||||
}
|
|
@ -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
Loading…
Reference in New Issue
Block a user