mirror of
https://github.com/raspberrypi/linux.git
synced 2025-12-27 04:22:58 +00:00
Merge tag 'bpf-next-6.15' of git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next
Pull bpf updates from Alexei Starovoitov:
"For this merge window we're splitting BPF pull request into three for
higher visibility: main changes, res_spin_lock, try_alloc_pages.
These are the main BPF changes:
- Add DFA-based live registers analysis to improve verification of
programs with loops (Eduard Zingerman)
- Introduce load_acquire and store_release BPF instructions and add
x86, arm64 JIT support (Peilin Ye)
- Fix loop detection logic in the verifier (Eduard Zingerman)
- Drop unnecesary lock in bpf_map_inc_not_zero() (Eric Dumazet)
- Add kfunc for populating cpumask bits (Emil Tsalapatis)
- Convert various shell based tests to selftests/bpf/test_progs
format (Bastien Curutchet)
- Allow passing referenced kptrs into struct_ops callbacks (Amery
Hung)
- Add a flag to LSM bpf hook to facilitate bpf program signing
(Blaise Boscaccy)
- Track arena arguments in kfuncs (Ihor Solodrai)
- Add copy_remote_vm_str() helper for reading strings from remote VM
and bpf_copy_from_user_task_str() kfunc (Jordan Rome)
- Add support for timed may_goto instruction (Kumar Kartikeya
Dwivedi)
- Allow bpf_get_netns_cookie() int cgroup_skb programs (Mahe Tardy)
- Reduce bpf_cgrp_storage_busy false positives when accessing cgroup
local storage (Martin KaFai Lau)
- Introduce bpf_dynptr_copy() kfunc (Mykyta Yatsenko)
- Allow retrieving BTF data with BTF token (Mykyta Yatsenko)
- Add BPF kfuncs to set and get xattrs with 'security.bpf.' prefix
(Song Liu)
- Reject attaching programs to noreturn functions (Yafang Shao)
- Introduce pre-order traversal of cgroup bpf programs (Yonghong
Song)"
* tag 'bpf-next-6.15' of git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next: (186 commits)
selftests/bpf: Add selftests for load-acquire/store-release when register number is invalid
bpf: Fix out-of-bounds read in check_atomic_load/store()
libbpf: Add namespace for errstr making it libbpf_errstr
bpf: Add struct_ops context information to struct bpf_prog_aux
selftests/bpf: Sanitize pointer prior fclose()
selftests/bpf: Migrate test_xdp_vlan.sh into test_progs
selftests/bpf: test_xdp_vlan: Rename BPF sections
bpf: clarify a misleading verifier error message
selftests/bpf: Add selftest for attaching fexit to __noreturn functions
bpf: Reject attaching fexit/fmod_ret to __noreturn functions
bpf: Only fails the busy counter check in bpf_cgrp_storage_get if it creates storage
bpf: Make perf_event_read_output accessible in all program types.
bpftool: Using the right format specifiers
bpftool: Add -Wformat-signedness flag to detect format errors
selftests/bpf: Test freplace from user namespace
libbpf: Pass BPF token from find_prog_btf_id to BPF_BTF_GET_FD_BY_ID
bpf: Return prog btf_id without capable check
bpf: BPF token support for BPF_BTF_GET_FD_BY_ID
bpf, x86: Fix objtool warning for timed may_goto
bpf: Check map->record at the beginning of check_and_free_fields()
...
This commit is contained in:
@@ -1,12 +1,3 @@
|
||||
bpf_cookie/multi_kprobe_attach_api # kprobe_multi_link_api_subtest:FAIL:fentry_raw_skel_load unexpected error: -3
|
||||
bpf_cookie/multi_kprobe_link_api # kprobe_multi_link_api_subtest:FAIL:fentry_raw_skel_load unexpected error: -3
|
||||
kprobe_multi_bench_attach # needs CONFIG_FPROBE
|
||||
kprobe_multi_test # needs CONFIG_FPROBE
|
||||
module_attach # prog 'kprobe_multi': failed to auto-attach: -95
|
||||
fentry_test/fentry_many_args # fentry_many_args:FAIL:fentry_many_args_attach unexpected error: -524
|
||||
fexit_test/fexit_many_args # fexit_many_args:FAIL:fexit_many_args_attach unexpected error: -524
|
||||
tracing_struct/struct_many_args # struct_many_args:FAIL:tracing_struct_many_args__attach unexpected error: -524
|
||||
fill_link_info/kprobe_multi_link_info # bpf_program__attach_kprobe_multi_opts unexpected error: -95
|
||||
fill_link_info/kretprobe_multi_link_info # bpf_program__attach_kprobe_multi_opts unexpected error: -95
|
||||
fill_link_info/kprobe_multi_invalid_ubuff # bpf_program__attach_kprobe_multi_opts unexpected error: -95
|
||||
missed/kprobe_recursion # missed_kprobe_recursion__attach unexpected error: -95 (errno 95)
|
||||
|
||||
@@ -95,18 +95,12 @@ TEST_GEN_PROGS += test_progs-cpuv4
|
||||
TEST_INST_SUBDIRS += cpuv4
|
||||
endif
|
||||
|
||||
TEST_GEN_FILES = test_lwt_ip_encap.bpf.o test_tc_edt.bpf.o
|
||||
TEST_GEN_FILES = test_tc_edt.bpf.o
|
||||
TEST_FILES = xsk_prereqs.sh $(wildcard progs/btf_dump_test_case_*.c)
|
||||
|
||||
# Order correspond to 'make run_tests' order
|
||||
TEST_PROGS := test_kmod.sh \
|
||||
test_xdp_redirect_multi.sh \
|
||||
test_tunnel.sh \
|
||||
test_lwt_seg6local.sh \
|
||||
test_lirc_mode2.sh \
|
||||
test_xdp_vlan_mode_generic.sh \
|
||||
test_xdp_vlan_mode_native.sh \
|
||||
test_lwt_ip_encap.sh \
|
||||
test_tc_tunnel.sh \
|
||||
test_tc_edt.sh \
|
||||
test_xdping.sh \
|
||||
@@ -117,9 +111,9 @@ TEST_PROGS := test_kmod.sh \
|
||||
test_xsk.sh \
|
||||
test_xdp_features.sh
|
||||
|
||||
TEST_PROGS_EXTENDED := with_addr.sh \
|
||||
with_tunnels.sh ima_setup.sh verify_sig_setup.sh \
|
||||
test_xdp_vlan.sh test_bpftool.py
|
||||
TEST_PROGS_EXTENDED := \
|
||||
ima_setup.sh verify_sig_setup.sh \
|
||||
test_bpftool.py
|
||||
|
||||
TEST_KMODS := bpf_testmod.ko bpf_test_no_cfi.ko bpf_test_modorder_x.ko \
|
||||
bpf_test_modorder_y.ko
|
||||
@@ -135,7 +129,6 @@ TEST_GEN_PROGS_EXTENDED = \
|
||||
veristat \
|
||||
xdp_features \
|
||||
xdp_hw_metadata \
|
||||
xdp_redirect_multi \
|
||||
xdp_synproxy \
|
||||
xdping \
|
||||
xskxceiver
|
||||
@@ -184,9 +177,14 @@ ifeq ($(feature-llvm),1)
|
||||
LLVM_CONFIG_LIB_COMPONENTS := mcdisassembler all-targets
|
||||
# both llvm-config and lib.mk add -D_GNU_SOURCE, which ends up as conflict
|
||||
LLVM_CFLAGS += $(filter-out -D_GNU_SOURCE,$(shell $(LLVM_CONFIG) --cflags))
|
||||
LLVM_LDLIBS += $(shell $(LLVM_CONFIG) --link-static --libs $(LLVM_CONFIG_LIB_COMPONENTS))
|
||||
LLVM_LDLIBS += $(shell $(LLVM_CONFIG) --link-static --system-libs $(LLVM_CONFIG_LIB_COMPONENTS))
|
||||
LLVM_LDLIBS += -lstdc++
|
||||
# Prefer linking statically if it's available, otherwise fallback to shared
|
||||
ifeq ($(shell $(LLVM_CONFIG) --link-static --libs >/dev/null 2>&1 && echo static),static)
|
||||
LLVM_LDLIBS += $(shell $(LLVM_CONFIG) --link-static --libs $(LLVM_CONFIG_LIB_COMPONENTS))
|
||||
LLVM_LDLIBS += $(shell $(LLVM_CONFIG) --link-static --system-libs $(LLVM_CONFIG_LIB_COMPONENTS))
|
||||
LLVM_LDLIBS += -lstdc++
|
||||
else
|
||||
LLVM_LDLIBS += $(shell $(LLVM_CONFIG) --link-shared --libs $(LLVM_CONFIG_LIB_COMPONENTS))
|
||||
endif
|
||||
LLVM_LDFLAGS += $(shell $(LLVM_CONFIG) --ldflags)
|
||||
endif
|
||||
|
||||
@@ -306,6 +304,7 @@ $(OUTPUT)/runqslower: $(BPFOBJ) | $(DEFAULT_BPFTOOL) $(RUNQSLOWER_OUTPUT)
|
||||
BPFTOOL_OUTPUT=$(HOST_BUILD_DIR)/bpftool/ \
|
||||
BPFOBJ_OUTPUT=$(BUILD_DIR)/libbpf/ \
|
||||
BPFOBJ=$(BPFOBJ) BPF_INCLUDE=$(INCLUDE_DIR) \
|
||||
BPF_TARGET_ENDIAN=$(BPF_TARGET_ENDIAN) \
|
||||
EXTRA_CFLAGS='-g $(OPT_FLAGS) $(SAN_CFLAGS) $(EXTRA_CFLAGS)' \
|
||||
EXTRA_LDFLAGS='$(SAN_LDFLAGS) $(EXTRA_LDFLAGS)' && \
|
||||
cp $(RUNQSLOWER_OUTPUT)runqslower $@
|
||||
@@ -684,6 +683,7 @@ $(OUTPUT)/$(TRUNNER_BINARY): $(TRUNNER_TEST_OBJS) \
|
||||
$(TRUNNER_EXTRA_OBJS) $$(BPFOBJ) \
|
||||
$(RESOLVE_BTFIDS) \
|
||||
$(TRUNNER_BPFTOOL) \
|
||||
$(OUTPUT)/veristat \
|
||||
| $(TRUNNER_BINARY)-extras
|
||||
$$(call msg,BINARY,,$$@)
|
||||
$(Q)$$(CC) $$(CFLAGS) $$(filter %.a %.o,$$^) $$(LDLIBS) $$(LDFLAGS) -o $$@
|
||||
|
||||
533
tools/testing/selftests/bpf/bpf_arena_spin_lock.h
Normal file
533
tools/testing/selftests/bpf/bpf_arena_spin_lock.h
Normal file
@@ -0,0 +1,533 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */
|
||||
#ifndef BPF_ARENA_SPIN_LOCK_H
|
||||
#define BPF_ARENA_SPIN_LOCK_H
|
||||
|
||||
#include <vmlinux.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include "bpf_atomic.h"
|
||||
|
||||
#define arch_mcs_spin_lock_contended_label(l, label) smp_cond_load_acquire_label(l, VAL, label)
|
||||
#define arch_mcs_spin_unlock_contended(l) smp_store_release((l), 1)
|
||||
|
||||
#if defined(ENABLE_ATOMICS_TESTS) && defined(__BPF_FEATURE_ADDR_SPACE_CAST)
|
||||
|
||||
#define EBUSY 16
|
||||
#define EOPNOTSUPP 95
|
||||
#define ETIMEDOUT 110
|
||||
|
||||
#ifndef __arena
|
||||
#define __arena __attribute__((address_space(1)))
|
||||
#endif
|
||||
|
||||
extern unsigned long CONFIG_NR_CPUS __kconfig;
|
||||
|
||||
/*
|
||||
* Typically, we'd just rely on the definition in vmlinux.h for qspinlock, but
|
||||
* PowerPC overrides the definition to define lock->val as u32 instead of
|
||||
* atomic_t, leading to compilation errors. Import a local definition below so
|
||||
* that we don't depend on the vmlinux.h version.
|
||||
*/
|
||||
|
||||
struct __qspinlock {
|
||||
union {
|
||||
atomic_t val;
|
||||
struct {
|
||||
u8 locked;
|
||||
u8 pending;
|
||||
};
|
||||
struct {
|
||||
u16 locked_pending;
|
||||
u16 tail;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
#define arena_spinlock_t struct __qspinlock
|
||||
/* FIXME: Using typedef causes CO-RE relocation error */
|
||||
/* typedef struct qspinlock arena_spinlock_t; */
|
||||
|
||||
struct arena_mcs_spinlock {
|
||||
struct arena_mcs_spinlock __arena *next;
|
||||
int locked;
|
||||
int count;
|
||||
};
|
||||
|
||||
struct arena_qnode {
|
||||
struct arena_mcs_spinlock mcs;
|
||||
};
|
||||
|
||||
#define _Q_MAX_NODES 4
|
||||
#define _Q_PENDING_LOOPS 1
|
||||
|
||||
/*
|
||||
* Bitfields in the atomic value:
|
||||
*
|
||||
* 0- 7: locked byte
|
||||
* 8: pending
|
||||
* 9-15: not used
|
||||
* 16-17: tail index
|
||||
* 18-31: tail cpu (+1)
|
||||
*/
|
||||
#define _Q_MAX_CPUS 1024
|
||||
|
||||
#define _Q_SET_MASK(type) (((1U << _Q_ ## type ## _BITS) - 1)\
|
||||
<< _Q_ ## type ## _OFFSET)
|
||||
#define _Q_LOCKED_OFFSET 0
|
||||
#define _Q_LOCKED_BITS 8
|
||||
#define _Q_LOCKED_MASK _Q_SET_MASK(LOCKED)
|
||||
|
||||
#define _Q_PENDING_OFFSET (_Q_LOCKED_OFFSET + _Q_LOCKED_BITS)
|
||||
#define _Q_PENDING_BITS 8
|
||||
#define _Q_PENDING_MASK _Q_SET_MASK(PENDING)
|
||||
|
||||
#define _Q_TAIL_IDX_OFFSET (_Q_PENDING_OFFSET + _Q_PENDING_BITS)
|
||||
#define _Q_TAIL_IDX_BITS 2
|
||||
#define _Q_TAIL_IDX_MASK _Q_SET_MASK(TAIL_IDX)
|
||||
|
||||
#define _Q_TAIL_CPU_OFFSET (_Q_TAIL_IDX_OFFSET + _Q_TAIL_IDX_BITS)
|
||||
#define _Q_TAIL_CPU_BITS (32 - _Q_TAIL_CPU_OFFSET)
|
||||
#define _Q_TAIL_CPU_MASK _Q_SET_MASK(TAIL_CPU)
|
||||
|
||||
#define _Q_TAIL_OFFSET _Q_TAIL_IDX_OFFSET
|
||||
#define _Q_TAIL_MASK (_Q_TAIL_IDX_MASK | _Q_TAIL_CPU_MASK)
|
||||
|
||||
#define _Q_LOCKED_VAL (1U << _Q_LOCKED_OFFSET)
|
||||
#define _Q_PENDING_VAL (1U << _Q_PENDING_OFFSET)
|
||||
|
||||
#define likely(x) __builtin_expect(!!(x), 1)
|
||||
#define unlikely(x) __builtin_expect(!!(x), 0)
|
||||
|
||||
struct arena_qnode __arena qnodes[_Q_MAX_CPUS][_Q_MAX_NODES];
|
||||
|
||||
static inline u32 encode_tail(int cpu, int idx)
|
||||
{
|
||||
u32 tail;
|
||||
|
||||
tail = (cpu + 1) << _Q_TAIL_CPU_OFFSET;
|
||||
tail |= idx << _Q_TAIL_IDX_OFFSET; /* assume < 4 */
|
||||
|
||||
return tail;
|
||||
}
|
||||
|
||||
static inline struct arena_mcs_spinlock __arena *decode_tail(u32 tail)
|
||||
{
|
||||
u32 cpu = (tail >> _Q_TAIL_CPU_OFFSET) - 1;
|
||||
u32 idx = (tail & _Q_TAIL_IDX_MASK) >> _Q_TAIL_IDX_OFFSET;
|
||||
|
||||
return &qnodes[cpu][idx].mcs;
|
||||
}
|
||||
|
||||
static inline
|
||||
struct arena_mcs_spinlock __arena *grab_mcs_node(struct arena_mcs_spinlock __arena *base, int idx)
|
||||
{
|
||||
return &((struct arena_qnode __arena *)base + idx)->mcs;
|
||||
}
|
||||
|
||||
#define _Q_LOCKED_PENDING_MASK (_Q_LOCKED_MASK | _Q_PENDING_MASK)
|
||||
|
||||
/**
|
||||
* xchg_tail - Put in the new queue tail code word & retrieve previous one
|
||||
* @lock : Pointer to queued spinlock structure
|
||||
* @tail : The new queue tail code word
|
||||
* Return: The previous queue tail code word
|
||||
*
|
||||
* xchg(lock, tail)
|
||||
*
|
||||
* p,*,* -> n,*,* ; prev = xchg(lock, node)
|
||||
*/
|
||||
static __always_inline u32 xchg_tail(arena_spinlock_t __arena *lock, u32 tail)
|
||||
{
|
||||
u32 old, new;
|
||||
|
||||
old = atomic_read(&lock->val);
|
||||
do {
|
||||
new = (old & _Q_LOCKED_PENDING_MASK) | tail;
|
||||
/*
|
||||
* We can use relaxed semantics since the caller ensures that
|
||||
* the MCS node is properly initialized before updating the
|
||||
* tail.
|
||||
*/
|
||||
/* These loops are not expected to stall, but we still need to
|
||||
* prove to the verifier they will terminate eventually.
|
||||
*/
|
||||
cond_break_label(out);
|
||||
} while (!atomic_try_cmpxchg_relaxed(&lock->val, &old, new));
|
||||
|
||||
return old;
|
||||
out:
|
||||
bpf_printk("RUNTIME ERROR: %s unexpected cond_break exit!!!", __func__);
|
||||
return old;
|
||||
}
|
||||
|
||||
/**
|
||||
* clear_pending - clear the pending bit.
|
||||
* @lock: Pointer to queued spinlock structure
|
||||
*
|
||||
* *,1,* -> *,0,*
|
||||
*/
|
||||
static __always_inline void clear_pending(arena_spinlock_t __arena *lock)
|
||||
{
|
||||
WRITE_ONCE(lock->pending, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* clear_pending_set_locked - take ownership and clear the pending bit.
|
||||
* @lock: Pointer to queued spinlock structure
|
||||
*
|
||||
* *,1,0 -> *,0,1
|
||||
*
|
||||
* Lock stealing is not allowed if this function is used.
|
||||
*/
|
||||
static __always_inline void clear_pending_set_locked(arena_spinlock_t __arena *lock)
|
||||
{
|
||||
WRITE_ONCE(lock->locked_pending, _Q_LOCKED_VAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* set_locked - Set the lock bit and own the lock
|
||||
* @lock: Pointer to queued spinlock structure
|
||||
*
|
||||
* *,*,0 -> *,0,1
|
||||
*/
|
||||
static __always_inline void set_locked(arena_spinlock_t __arena *lock)
|
||||
{
|
||||
WRITE_ONCE(lock->locked, _Q_LOCKED_VAL);
|
||||
}
|
||||
|
||||
static __always_inline
|
||||
u32 arena_fetch_set_pending_acquire(arena_spinlock_t __arena *lock)
|
||||
{
|
||||
u32 old, new;
|
||||
|
||||
old = atomic_read(&lock->val);
|
||||
do {
|
||||
new = old | _Q_PENDING_VAL;
|
||||
/*
|
||||
* These loops are not expected to stall, but we still need to
|
||||
* prove to the verifier they will terminate eventually.
|
||||
*/
|
||||
cond_break_label(out);
|
||||
} while (!atomic_try_cmpxchg_acquire(&lock->val, &old, new));
|
||||
|
||||
return old;
|
||||
out:
|
||||
bpf_printk("RUNTIME ERROR: %s unexpected cond_break exit!!!", __func__);
|
||||
return old;
|
||||
}
|
||||
|
||||
/**
|
||||
* arena_spin_trylock - try to acquire the queued spinlock
|
||||
* @lock : Pointer to queued spinlock structure
|
||||
* Return: 1 if lock acquired, 0 if failed
|
||||
*/
|
||||
static __always_inline int arena_spin_trylock(arena_spinlock_t __arena *lock)
|
||||
{
|
||||
int val = atomic_read(&lock->val);
|
||||
|
||||
if (unlikely(val))
|
||||
return 0;
|
||||
|
||||
return likely(atomic_try_cmpxchg_acquire(&lock->val, &val, _Q_LOCKED_VAL));
|
||||
}
|
||||
|
||||
__noinline
|
||||
int arena_spin_lock_slowpath(arena_spinlock_t __arena __arg_arena *lock, u32 val)
|
||||
{
|
||||
struct arena_mcs_spinlock __arena *prev, *next, *node0, *node;
|
||||
int ret = -ETIMEDOUT;
|
||||
u32 old, tail;
|
||||
int idx;
|
||||
|
||||
/*
|
||||
* Wait for in-progress pending->locked hand-overs with a bounded
|
||||
* number of spins so that we guarantee forward progress.
|
||||
*
|
||||
* 0,1,0 -> 0,0,1
|
||||
*/
|
||||
if (val == _Q_PENDING_VAL) {
|
||||
int cnt = _Q_PENDING_LOOPS;
|
||||
val = atomic_cond_read_relaxed_label(&lock->val,
|
||||
(VAL != _Q_PENDING_VAL) || !cnt--,
|
||||
release_err);
|
||||
}
|
||||
|
||||
/*
|
||||
* If we observe any contention; queue.
|
||||
*/
|
||||
if (val & ~_Q_LOCKED_MASK)
|
||||
goto queue;
|
||||
|
||||
/*
|
||||
* trylock || pending
|
||||
*
|
||||
* 0,0,* -> 0,1,* -> 0,0,1 pending, trylock
|
||||
*/
|
||||
val = arena_fetch_set_pending_acquire(lock);
|
||||
|
||||
/*
|
||||
* If we observe contention, there is a concurrent locker.
|
||||
*
|
||||
* Undo and queue; our setting of PENDING might have made the
|
||||
* n,0,0 -> 0,0,0 transition fail and it will now be waiting
|
||||
* on @next to become !NULL.
|
||||
*/
|
||||
if (unlikely(val & ~_Q_LOCKED_MASK)) {
|
||||
|
||||
/* Undo PENDING if we set it. */
|
||||
if (!(val & _Q_PENDING_MASK))
|
||||
clear_pending(lock);
|
||||
|
||||
goto queue;
|
||||
}
|
||||
|
||||
/*
|
||||
* We're pending, wait for the owner to go away.
|
||||
*
|
||||
* 0,1,1 -> *,1,0
|
||||
*
|
||||
* this wait loop must be a load-acquire such that we match the
|
||||
* store-release that clears the locked bit and create lock
|
||||
* sequentiality; this is because not all
|
||||
* clear_pending_set_locked() implementations imply full
|
||||
* barriers.
|
||||
*/
|
||||
if (val & _Q_LOCKED_MASK)
|
||||
smp_cond_load_acquire_label(&lock->locked, !VAL, release_err);
|
||||
|
||||
/*
|
||||
* take ownership and clear the pending bit.
|
||||
*
|
||||
* 0,1,0 -> 0,0,1
|
||||
*/
|
||||
clear_pending_set_locked(lock);
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* End of pending bit optimistic spinning and beginning of MCS
|
||||
* queuing.
|
||||
*/
|
||||
queue:
|
||||
node0 = &(qnodes[bpf_get_smp_processor_id()])[0].mcs;
|
||||
idx = node0->count++;
|
||||
tail = encode_tail(bpf_get_smp_processor_id(), idx);
|
||||
|
||||
/*
|
||||
* 4 nodes are allocated based on the assumption that there will not be
|
||||
* nested NMIs taking spinlocks. That may not be true in some
|
||||
* architectures even though the chance of needing more than 4 nodes
|
||||
* will still be extremely unlikely. When that happens, we simply return
|
||||
* an error. Original qspinlock has a trylock fallback in this case.
|
||||
*/
|
||||
if (unlikely(idx >= _Q_MAX_NODES)) {
|
||||
ret = -EBUSY;
|
||||
goto release_node_err;
|
||||
}
|
||||
|
||||
node = grab_mcs_node(node0, idx);
|
||||
|
||||
/*
|
||||
* Ensure that we increment the head node->count before initialising
|
||||
* the actual node. If the compiler is kind enough to reorder these
|
||||
* stores, then an IRQ could overwrite our assignments.
|
||||
*/
|
||||
barrier();
|
||||
|
||||
node->locked = 0;
|
||||
node->next = NULL;
|
||||
|
||||
/*
|
||||
* We touched a (possibly) cold cacheline in the per-cpu queue node;
|
||||
* attempt the trylock once more in the hope someone let go while we
|
||||
* weren't watching.
|
||||
*/
|
||||
if (arena_spin_trylock(lock))
|
||||
goto release;
|
||||
|
||||
/*
|
||||
* Ensure that the initialisation of @node is complete before we
|
||||
* publish the updated tail via xchg_tail() and potentially link
|
||||
* @node into the waitqueue via WRITE_ONCE(prev->next, node) below.
|
||||
*/
|
||||
smp_wmb();
|
||||
|
||||
/*
|
||||
* Publish the updated tail.
|
||||
* We have already touched the queueing cacheline; don't bother with
|
||||
* pending stuff.
|
||||
*
|
||||
* p,*,* -> n,*,*
|
||||
*/
|
||||
old = xchg_tail(lock, tail);
|
||||
next = NULL;
|
||||
|
||||
/*
|
||||
* if there was a previous node; link it and wait until reaching the
|
||||
* head of the waitqueue.
|
||||
*/
|
||||
if (old & _Q_TAIL_MASK) {
|
||||
prev = decode_tail(old);
|
||||
|
||||
/* Link @node into the waitqueue. */
|
||||
WRITE_ONCE(prev->next, node);
|
||||
|
||||
arch_mcs_spin_lock_contended_label(&node->locked, release_node_err);
|
||||
|
||||
/*
|
||||
* While waiting for the MCS lock, the next pointer may have
|
||||
* been set by another lock waiter. We cannot prefetch here
|
||||
* due to lack of equivalent instruction in BPF ISA.
|
||||
*/
|
||||
next = READ_ONCE(node->next);
|
||||
}
|
||||
|
||||
/*
|
||||
* we're at the head of the waitqueue, wait for the owner & pending to
|
||||
* go away.
|
||||
*
|
||||
* *,x,y -> *,0,0
|
||||
*
|
||||
* this wait loop must use a load-acquire such that we match the
|
||||
* store-release that clears the locked bit and create lock
|
||||
* sequentiality; this is because the set_locked() function below
|
||||
* does not imply a full barrier.
|
||||
*/
|
||||
val = atomic_cond_read_acquire_label(&lock->val, !(VAL & _Q_LOCKED_PENDING_MASK),
|
||||
release_node_err);
|
||||
|
||||
/*
|
||||
* claim the lock:
|
||||
*
|
||||
* n,0,0 -> 0,0,1 : lock, uncontended
|
||||
* *,*,0 -> *,*,1 : lock, contended
|
||||
*
|
||||
* If the queue head is the only one in the queue (lock value == tail)
|
||||
* and nobody is pending, clear the tail code and grab the lock.
|
||||
* Otherwise, we only need to grab the lock.
|
||||
*/
|
||||
|
||||
/*
|
||||
* In the PV case we might already have _Q_LOCKED_VAL set, because
|
||||
* of lock stealing; therefore we must also allow:
|
||||
*
|
||||
* n,0,1 -> 0,0,1
|
||||
*
|
||||
* Note: at this point: (val & _Q_PENDING_MASK) == 0, because of the
|
||||
* above wait condition, therefore any concurrent setting of
|
||||
* PENDING will make the uncontended transition fail.
|
||||
*/
|
||||
if ((val & _Q_TAIL_MASK) == tail) {
|
||||
if (atomic_try_cmpxchg_relaxed(&lock->val, &val, _Q_LOCKED_VAL))
|
||||
goto release; /* No contention */
|
||||
}
|
||||
|
||||
/*
|
||||
* Either somebody is queued behind us or _Q_PENDING_VAL got set
|
||||
* which will then detect the remaining tail and queue behind us
|
||||
* ensuring we'll see a @next.
|
||||
*/
|
||||
set_locked(lock);
|
||||
|
||||
/*
|
||||
* contended path; wait for next if not observed yet, release.
|
||||
*/
|
||||
if (!next)
|
||||
next = smp_cond_load_relaxed_label(&node->next, (VAL), release_node_err);
|
||||
|
||||
arch_mcs_spin_unlock_contended(&next->locked);
|
||||
|
||||
release:;
|
||||
/*
|
||||
* release the node
|
||||
*
|
||||
* Doing a normal dec vs this_cpu_dec is fine. An upper context always
|
||||
* decrements count it incremented before returning, thus we're fine.
|
||||
* For contexts interrupting us, they either observe our dec or not.
|
||||
* Just ensure the compiler doesn't reorder this statement, as a
|
||||
* this_cpu_dec implicitly implied that.
|
||||
*/
|
||||
barrier();
|
||||
node0->count--;
|
||||
return 0;
|
||||
release_node_err:
|
||||
barrier();
|
||||
node0->count--;
|
||||
goto release_err;
|
||||
release_err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* arena_spin_lock - acquire a queued spinlock
|
||||
* @lock: Pointer to queued spinlock structure
|
||||
*
|
||||
* On error, returned value will be negative.
|
||||
* On success, zero is returned.
|
||||
*
|
||||
* The return value _must_ be tested against zero for success,
|
||||
* instead of checking it against negative, for passing the
|
||||
* BPF verifier.
|
||||
*
|
||||
* The user should do:
|
||||
* if (arena_spin_lock(...) != 0) // failure
|
||||
* or
|
||||
* if (arena_spin_lock(...) == 0) // success
|
||||
* or
|
||||
* if (arena_spin_lock(...)) // failure
|
||||
* or
|
||||
* if (!arena_spin_lock(...)) // success
|
||||
* instead of:
|
||||
* if (arena_spin_lock(...) < 0) // failure
|
||||
*
|
||||
* The return value can still be inspected later.
|
||||
*/
|
||||
static __always_inline int arena_spin_lock(arena_spinlock_t __arena *lock)
|
||||
{
|
||||
int val = 0;
|
||||
|
||||
if (CONFIG_NR_CPUS > 1024)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
bpf_preempt_disable();
|
||||
if (likely(atomic_try_cmpxchg_acquire(&lock->val, &val, _Q_LOCKED_VAL)))
|
||||
return 0;
|
||||
|
||||
val = arena_spin_lock_slowpath(lock, val);
|
||||
/* FIXME: bpf_assert_range(-MAX_ERRNO, 0) once we have it working for all cases. */
|
||||
if (val)
|
||||
bpf_preempt_enable();
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* arena_spin_unlock - release a queued spinlock
|
||||
* @lock : Pointer to queued spinlock structure
|
||||
*/
|
||||
static __always_inline void arena_spin_unlock(arena_spinlock_t __arena *lock)
|
||||
{
|
||||
/*
|
||||
* unlock() needs release semantics:
|
||||
*/
|
||||
smp_store_release(&lock->locked, 0);
|
||||
bpf_preempt_enable();
|
||||
}
|
||||
|
||||
#define arena_spin_lock_irqsave(lock, flags) \
|
||||
({ \
|
||||
int __ret; \
|
||||
bpf_local_irq_save(&(flags)); \
|
||||
__ret = arena_spin_lock((lock)); \
|
||||
if (__ret) \
|
||||
bpf_local_irq_restore(&(flags)); \
|
||||
(__ret); \
|
||||
})
|
||||
|
||||
#define arena_spin_unlock_irqrestore(lock, flags) \
|
||||
({ \
|
||||
arena_spin_unlock((lock)); \
|
||||
bpf_local_irq_restore(&(flags)); \
|
||||
})
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* BPF_ARENA_SPIN_LOCK_H */
|
||||
140
tools/testing/selftests/bpf/bpf_atomic.h
Normal file
140
tools/testing/selftests/bpf/bpf_atomic.h
Normal file
@@ -0,0 +1,140 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */
|
||||
#ifndef BPF_ATOMIC_H
|
||||
#define BPF_ATOMIC_H
|
||||
|
||||
#include <vmlinux.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include "bpf_experimental.h"
|
||||
|
||||
extern bool CONFIG_X86_64 __kconfig __weak;
|
||||
|
||||
/*
|
||||
* __unqual_typeof(x) - Declare an unqualified scalar type, leaving
|
||||
* non-scalar types unchanged,
|
||||
*
|
||||
* Prefer C11 _Generic for better compile-times and simpler code. Note: 'char'
|
||||
* is not type-compatible with 'signed char', and we define a separate case.
|
||||
*
|
||||
* This is copied verbatim from kernel's include/linux/compiler_types.h, but
|
||||
* with default expression (for pointers) changed from (x) to (typeof(x)0).
|
||||
*
|
||||
* This is because LLVM has a bug where for lvalue (x), it does not get rid of
|
||||
* an extra address_space qualifier, but does in case of rvalue (typeof(x)0).
|
||||
* Hence, for pointers, we need to create an rvalue expression to get the
|
||||
* desired type. See https://github.com/llvm/llvm-project/issues/53400.
|
||||
*/
|
||||
#define __scalar_type_to_expr_cases(type) \
|
||||
unsigned type : (unsigned type)0, signed type : (signed type)0
|
||||
|
||||
#define __unqual_typeof(x) \
|
||||
typeof(_Generic((x), \
|
||||
char: (char)0, \
|
||||
__scalar_type_to_expr_cases(char), \
|
||||
__scalar_type_to_expr_cases(short), \
|
||||
__scalar_type_to_expr_cases(int), \
|
||||
__scalar_type_to_expr_cases(long), \
|
||||
__scalar_type_to_expr_cases(long long), \
|
||||
default: (typeof(x))0))
|
||||
|
||||
/* No-op for BPF */
|
||||
#define cpu_relax() ({})
|
||||
|
||||
#define READ_ONCE(x) (*(volatile typeof(x) *)&(x))
|
||||
|
||||
#define WRITE_ONCE(x, val) ((*(volatile typeof(x) *)&(x)) = (val))
|
||||
|
||||
#define cmpxchg(p, old, new) __sync_val_compare_and_swap((p), old, new)
|
||||
|
||||
#define try_cmpxchg(p, pold, new) \
|
||||
({ \
|
||||
__unqual_typeof(*(pold)) __o = *(pold); \
|
||||
__unqual_typeof(*(p)) __r = cmpxchg(p, __o, new); \
|
||||
if (__r != __o) \
|
||||
*(pold) = __r; \
|
||||
__r == __o; \
|
||||
})
|
||||
|
||||
#define try_cmpxchg_relaxed(p, pold, new) try_cmpxchg(p, pold, new)
|
||||
|
||||
#define try_cmpxchg_acquire(p, pold, new) try_cmpxchg(p, pold, new)
|
||||
|
||||
#define smp_mb() \
|
||||
({ \
|
||||
unsigned long __val; \
|
||||
__sync_fetch_and_add(&__val, 0); \
|
||||
})
|
||||
|
||||
#define smp_rmb() \
|
||||
({ \
|
||||
if (!CONFIG_X86_64) \
|
||||
smp_mb(); \
|
||||
else \
|
||||
barrier(); \
|
||||
})
|
||||
|
||||
#define smp_wmb() \
|
||||
({ \
|
||||
if (!CONFIG_X86_64) \
|
||||
smp_mb(); \
|
||||
else \
|
||||
barrier(); \
|
||||
})
|
||||
|
||||
/* Control dependency provides LOAD->STORE, provide LOAD->LOAD */
|
||||
#define smp_acquire__after_ctrl_dep() ({ smp_rmb(); })
|
||||
|
||||
#define smp_load_acquire(p) \
|
||||
({ \
|
||||
__unqual_typeof(*(p)) __v = READ_ONCE(*(p)); \
|
||||
if (!CONFIG_X86_64) \
|
||||
smp_mb(); \
|
||||
barrier(); \
|
||||
__v; \
|
||||
})
|
||||
|
||||
#define smp_store_release(p, val) \
|
||||
({ \
|
||||
if (!CONFIG_X86_64) \
|
||||
smp_mb(); \
|
||||
barrier(); \
|
||||
WRITE_ONCE(*(p), val); \
|
||||
})
|
||||
|
||||
#define smp_cond_load_relaxed_label(p, cond_expr, label) \
|
||||
({ \
|
||||
typeof(p) __ptr = (p); \
|
||||
__unqual_typeof(*(p)) VAL; \
|
||||
for (;;) { \
|
||||
VAL = (__unqual_typeof(*(p)))READ_ONCE(*__ptr); \
|
||||
if (cond_expr) \
|
||||
break; \
|
||||
cond_break_label(label); \
|
||||
cpu_relax(); \
|
||||
} \
|
||||
(typeof(*(p)))VAL; \
|
||||
})
|
||||
|
||||
#define smp_cond_load_acquire_label(p, cond_expr, label) \
|
||||
({ \
|
||||
__unqual_typeof(*p) __val = \
|
||||
smp_cond_load_relaxed_label(p, cond_expr, label); \
|
||||
smp_acquire__after_ctrl_dep(); \
|
||||
(typeof(*(p)))__val; \
|
||||
})
|
||||
|
||||
#define atomic_read(p) READ_ONCE((p)->counter)
|
||||
|
||||
#define atomic_cond_read_relaxed_label(p, cond_expr, label) \
|
||||
smp_cond_load_relaxed_label(&(p)->counter, cond_expr, label)
|
||||
|
||||
#define atomic_cond_read_acquire_label(p, cond_expr, label) \
|
||||
smp_cond_load_acquire_label(&(p)->counter, cond_expr, label)
|
||||
|
||||
#define atomic_try_cmpxchg_relaxed(p, pold, new) \
|
||||
try_cmpxchg_relaxed(&(p)->counter, pold, new)
|
||||
|
||||
#define atomic_try_cmpxchg_acquire(p, pold, new) \
|
||||
try_cmpxchg_acquire(&(p)->counter, pold, new)
|
||||
|
||||
#endif /* BPF_ATOMIC_H */
|
||||
@@ -368,12 +368,12 @@ l_true: \
|
||||
ret; \
|
||||
})
|
||||
|
||||
#define cond_break \
|
||||
#define __cond_break(expr) \
|
||||
({ __label__ l_break, l_continue; \
|
||||
asm volatile goto("may_goto %l[l_break]" \
|
||||
:::: l_break); \
|
||||
goto l_continue; \
|
||||
l_break: break; \
|
||||
l_break: expr; \
|
||||
l_continue:; \
|
||||
})
|
||||
#else
|
||||
@@ -392,7 +392,7 @@ l_true: \
|
||||
ret; \
|
||||
})
|
||||
|
||||
#define cond_break \
|
||||
#define __cond_break(expr) \
|
||||
({ __label__ l_break, l_continue; \
|
||||
asm volatile goto("1:.byte 0xe5; \
|
||||
.byte 0; \
|
||||
@@ -400,7 +400,7 @@ l_true: \
|
||||
.short 0" \
|
||||
:::: l_break); \
|
||||
goto l_continue; \
|
||||
l_break: break; \
|
||||
l_break: expr; \
|
||||
l_continue:; \
|
||||
})
|
||||
#else
|
||||
@@ -418,7 +418,7 @@ l_true: \
|
||||
ret; \
|
||||
})
|
||||
|
||||
#define cond_break \
|
||||
#define __cond_break(expr) \
|
||||
({ __label__ l_break, l_continue; \
|
||||
asm volatile goto("1:.byte 0xe5; \
|
||||
.byte 0; \
|
||||
@@ -426,12 +426,15 @@ l_true: \
|
||||
.short 0" \
|
||||
:::: l_break); \
|
||||
goto l_continue; \
|
||||
l_break: break; \
|
||||
l_break: expr; \
|
||||
l_continue:; \
|
||||
})
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define cond_break __cond_break(break)
|
||||
#define cond_break_label(label) __cond_break(goto label)
|
||||
|
||||
#ifndef bpf_nop_mov
|
||||
#define bpf_nop_mov(var) \
|
||||
asm volatile("%[reg]=%[reg]"::[reg]"r"((short)var))
|
||||
|
||||
@@ -87,4 +87,9 @@ struct dentry;
|
||||
*/
|
||||
extern int bpf_get_dentry_xattr(struct dentry *dentry, const char *name,
|
||||
struct bpf_dynptr *value_ptr) __ksym __weak;
|
||||
|
||||
extern int bpf_set_dentry_xattr(struct dentry *dentry, const char *name__str,
|
||||
const struct bpf_dynptr *value_p, int flags) __ksym __weak;
|
||||
extern int bpf_remove_dentry_xattr(struct dentry *dentry, const char *name__str) __ksym __weak;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -19,7 +19,7 @@ int cap_enable_effective(__u64 caps, __u64 *old_caps)
|
||||
|
||||
err = capget(&hdr, data);
|
||||
if (err)
|
||||
return err;
|
||||
return -errno;
|
||||
|
||||
if (old_caps)
|
||||
*old_caps = (__u64)(data[1].effective) << 32 | data[0].effective;
|
||||
@@ -32,7 +32,7 @@ int cap_enable_effective(__u64 caps, __u64 *old_caps)
|
||||
data[1].effective |= cap1;
|
||||
err = capset(&hdr, data);
|
||||
if (err)
|
||||
return err;
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -49,7 +49,7 @@ int cap_disable_effective(__u64 caps, __u64 *old_caps)
|
||||
|
||||
err = capget(&hdr, data);
|
||||
if (err)
|
||||
return err;
|
||||
return -errno;
|
||||
|
||||
if (old_caps)
|
||||
*old_caps = (__u64)(data[1].effective) << 32 | data[0].effective;
|
||||
@@ -61,7 +61,7 @@ int cap_disable_effective(__u64 caps, __u64 *old_caps)
|
||||
data[1].effective &= ~cap1;
|
||||
err = capset(&hdr, data);
|
||||
if (err)
|
||||
return err;
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/capability.h>
|
||||
#include <errno.h>
|
||||
|
||||
#ifndef CAP_PERFMON
|
||||
#define CAP_PERFMON 38
|
||||
|
||||
@@ -446,6 +446,23 @@ char *ping_command(int family)
|
||||
return "ping";
|
||||
}
|
||||
|
||||
int append_tid(char *str, size_t sz)
|
||||
{
|
||||
size_t end;
|
||||
|
||||
if (!str)
|
||||
return -1;
|
||||
|
||||
end = strlen(str);
|
||||
if (end + 8 > sz)
|
||||
return -1;
|
||||
|
||||
sprintf(&str[end], "%07d", gettid());
|
||||
str[end + 7] = '\0';
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int remove_netns(const char *name)
|
||||
{
|
||||
char *cmd;
|
||||
@@ -761,6 +778,36 @@ struct tmonitor_ctx {
|
||||
int pcap_fd;
|
||||
};
|
||||
|
||||
static int __base_pr(const char *format, va_list args)
|
||||
{
|
||||
return vfprintf(stdout, format, args);
|
||||
}
|
||||
|
||||
static tm_print_fn_t __tm_pr = __base_pr;
|
||||
|
||||
tm_print_fn_t traffic_monitor_set_print(tm_print_fn_t fn)
|
||||
{
|
||||
tm_print_fn_t old_print_fn;
|
||||
|
||||
old_print_fn = __atomic_exchange_n(&__tm_pr, fn, __ATOMIC_RELAXED);
|
||||
|
||||
return old_print_fn;
|
||||
}
|
||||
|
||||
void tm_print(const char *format, ...)
|
||||
{
|
||||
tm_print_fn_t print_fn;
|
||||
va_list args;
|
||||
|
||||
print_fn = __atomic_load_n(&__tm_pr, __ATOMIC_RELAXED);
|
||||
if (!print_fn)
|
||||
return;
|
||||
|
||||
va_start(args, format);
|
||||
print_fn(format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
/* Is this packet captured with a Ethernet protocol type? */
|
||||
static bool is_ethernet(const u_char *packet)
|
||||
{
|
||||
@@ -778,7 +825,7 @@ static bool is_ethernet(const u_char *packet)
|
||||
case 770: /* ARPHRD_FRAD */
|
||||
case 778: /* ARPHDR_IPGRE */
|
||||
case 803: /* ARPHRD_IEEE80211_RADIOTAP */
|
||||
printf("Packet captured: arphdr_type=%d\n", arphdr_type);
|
||||
tm_print("Packet captured: arphdr_type=%d\n", arphdr_type);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -799,12 +846,13 @@ static const char *pkt_type_str(u16 pkt_type)
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
#define MAX_FLAGS_STRLEN 21
|
||||
/* Show the information of the transport layer in the packet */
|
||||
static void show_transport(const u_char *packet, u16 len, u32 ifindex,
|
||||
const char *src_addr, const char *dst_addr,
|
||||
u16 proto, bool ipv6, u8 pkt_type)
|
||||
{
|
||||
char *ifname, _ifname[IF_NAMESIZE];
|
||||
char *ifname, _ifname[IF_NAMESIZE], flags[MAX_FLAGS_STRLEN] = "";
|
||||
const char *transport_str;
|
||||
u16 src_port, dst_port;
|
||||
struct udphdr *udp;
|
||||
@@ -827,47 +875,39 @@ static void show_transport(const u_char *packet, u16 len, u32 ifindex,
|
||||
dst_port = ntohs(tcp->dest);
|
||||
transport_str = "TCP";
|
||||
} else if (proto == IPPROTO_ICMP) {
|
||||
printf("%-7s %-3s IPv4 %s > %s: ICMP, length %d, type %d, code %d\n",
|
||||
ifname, pkt_type_str(pkt_type), src_addr, dst_addr, len,
|
||||
packet[0], packet[1]);
|
||||
tm_print("%-7s %-3s IPv4 %s > %s: ICMP, length %d, type %d, code %d\n",
|
||||
ifname, pkt_type_str(pkt_type), src_addr, dst_addr, len,
|
||||
packet[0], packet[1]);
|
||||
return;
|
||||
} else if (proto == IPPROTO_ICMPV6) {
|
||||
printf("%-7s %-3s IPv6 %s > %s: ICMPv6, length %d, type %d, code %d\n",
|
||||
ifname, pkt_type_str(pkt_type), src_addr, dst_addr, len,
|
||||
packet[0], packet[1]);
|
||||
tm_print("%-7s %-3s IPv6 %s > %s: ICMPv6, length %d, type %d, code %d\n",
|
||||
ifname, pkt_type_str(pkt_type), src_addr, dst_addr, len,
|
||||
packet[0], packet[1]);
|
||||
return;
|
||||
} else {
|
||||
printf("%-7s %-3s %s %s > %s: protocol %d\n",
|
||||
ifname, pkt_type_str(pkt_type), ipv6 ? "IPv6" : "IPv4",
|
||||
src_addr, dst_addr, proto);
|
||||
tm_print("%-7s %-3s %s %s > %s: protocol %d\n",
|
||||
ifname, pkt_type_str(pkt_type), ipv6 ? "IPv6" : "IPv4",
|
||||
src_addr, dst_addr, proto);
|
||||
return;
|
||||
}
|
||||
|
||||
/* TCP or UDP*/
|
||||
|
||||
flockfile(stdout);
|
||||
if (proto == IPPROTO_TCP)
|
||||
snprintf(flags, MAX_FLAGS_STRLEN, "%s%s%s%s",
|
||||
tcp->fin ? ", FIN" : "",
|
||||
tcp->syn ? ", SYN" : "",
|
||||
tcp->rst ? ", RST" : "",
|
||||
tcp->ack ? ", ACK" : "");
|
||||
|
||||
if (ipv6)
|
||||
printf("%-7s %-3s IPv6 %s.%d > %s.%d: %s, length %d",
|
||||
ifname, pkt_type_str(pkt_type), src_addr, src_port,
|
||||
dst_addr, dst_port, transport_str, len);
|
||||
tm_print("%-7s %-3s IPv6 %s.%d > %s.%d: %s, length %d%s\n",
|
||||
ifname, pkt_type_str(pkt_type), src_addr, src_port,
|
||||
dst_addr, dst_port, transport_str, len, flags);
|
||||
else
|
||||
printf("%-7s %-3s IPv4 %s:%d > %s:%d: %s, length %d",
|
||||
ifname, pkt_type_str(pkt_type), src_addr, src_port,
|
||||
dst_addr, dst_port, transport_str, len);
|
||||
|
||||
if (proto == IPPROTO_TCP) {
|
||||
if (tcp->fin)
|
||||
printf(", FIN");
|
||||
if (tcp->syn)
|
||||
printf(", SYN");
|
||||
if (tcp->rst)
|
||||
printf(", RST");
|
||||
if (tcp->ack)
|
||||
printf(", ACK");
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
funlockfile(stdout);
|
||||
tm_print("%-7s %-3s IPv4 %s:%d > %s:%d: %s, length %d%s\n",
|
||||
ifname, pkt_type_str(pkt_type), src_addr, src_port,
|
||||
dst_addr, dst_port, transport_str, len, flags);
|
||||
}
|
||||
|
||||
static void show_ipv6_packet(const u_char *packet, u32 ifindex, u8 pkt_type)
|
||||
@@ -982,8 +1022,8 @@ static void *traffic_monitor_thread(void *arg)
|
||||
ifname = _ifname;
|
||||
}
|
||||
|
||||
printf("%-7s %-3s Unknown network protocol type 0x%x\n",
|
||||
ifname, pkt_type_str(ptype), proto);
|
||||
tm_print("%-7s %-3s Unknown network protocol type 0x%x\n",
|
||||
ifname, pkt_type_str(ptype), proto);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1183,8 +1223,9 @@ void traffic_monitor_stop(struct tmonitor_ctx *ctx)
|
||||
write(ctx->wake_fd, &w, sizeof(w));
|
||||
pthread_join(ctx->thread, NULL);
|
||||
|
||||
printf("Packet file: %s\n", strrchr(ctx->pkt_fname, '/') + 1);
|
||||
tm_print("Packet file: %s\n", strrchr(ctx->pkt_fname, '/') + 1);
|
||||
|
||||
traffic_monitor_release(ctx);
|
||||
}
|
||||
|
||||
#endif /* TRAFFIC_MONITOR */
|
||||
|
||||
@@ -18,6 +18,7 @@ typedef __u16 __sum16;
|
||||
#include <netinet/udp.h>
|
||||
#include <bpf/bpf_endian.h>
|
||||
#include <net/if.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define MAGIC_VAL 0x1234
|
||||
#define NUM_ITER 100000
|
||||
@@ -101,6 +102,18 @@ int send_recv_data(int lfd, int fd, uint32_t total_bytes);
|
||||
int make_netns(const char *name);
|
||||
int remove_netns(const char *name);
|
||||
|
||||
/**
|
||||
* append_tid() - Append thread ID to the given string.
|
||||
*
|
||||
* @str: string to extend
|
||||
* @sz: string's size
|
||||
*
|
||||
* 8 characters are used to append the thread ID (7 digits + '\0')
|
||||
*
|
||||
* Returns -1 on errors, 0 otherwise
|
||||
*/
|
||||
int append_tid(char *str, size_t sz);
|
||||
|
||||
static __u16 csum_fold(__u32 csum)
|
||||
{
|
||||
csum = (csum & 0xffff) + (csum >> 16);
|
||||
@@ -240,10 +253,13 @@ static inline __sum16 build_udp_v6_csum(const struct ipv6hdr *ip6h,
|
||||
|
||||
struct tmonitor_ctx;
|
||||
|
||||
typedef int (*tm_print_fn_t)(const char *format, va_list args);
|
||||
|
||||
#ifdef TRAFFIC_MONITOR
|
||||
struct tmonitor_ctx *traffic_monitor_start(const char *netns, const char *test_name,
|
||||
const char *subtest_name);
|
||||
void traffic_monitor_stop(struct tmonitor_ctx *ctx);
|
||||
tm_print_fn_t traffic_monitor_set_print(tm_print_fn_t fn);
|
||||
#else
|
||||
static inline struct tmonitor_ctx *traffic_monitor_start(const char *netns, const char *test_name,
|
||||
const char *subtest_name)
|
||||
@@ -254,6 +270,11 @@ static inline struct tmonitor_ctx *traffic_monitor_start(const char *netns, cons
|
||||
static inline void traffic_monitor_stop(struct tmonitor_ctx *ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static inline tm_print_fn_t traffic_monitor_set_print(tm_print_fn_t fn)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -610,9 +610,11 @@ static int do_test_single(struct bpf_align_test *test)
|
||||
.log_size = sizeof(bpf_vlog),
|
||||
.log_level = 2,
|
||||
);
|
||||
const char *main_pass_start = "0: R1=ctx() R10=fp0";
|
||||
const char *line_ptr;
|
||||
int cur_line = -1;
|
||||
int prog_len, i;
|
||||
char *start;
|
||||
int fd_prog;
|
||||
int ret;
|
||||
|
||||
@@ -632,7 +634,13 @@ static int do_test_single(struct bpf_align_test *test)
|
||||
ret = 0;
|
||||
/* We make a local copy so that we can strtok() it */
|
||||
strncpy(bpf_vlog_copy, bpf_vlog, sizeof(bpf_vlog_copy));
|
||||
line_ptr = strtok(bpf_vlog_copy, "\n");
|
||||
start = strstr(bpf_vlog_copy, main_pass_start);
|
||||
if (!start) {
|
||||
ret = 1;
|
||||
printf("Can't find initial line '%s'\n", main_pass_start);
|
||||
goto out;
|
||||
}
|
||||
line_ptr = strtok(start, "\n");
|
||||
for (i = 0; i < MAX_MATCHES; i++) {
|
||||
struct bpf_reg_match m = test->matches[i];
|
||||
const char *p;
|
||||
@@ -682,6 +690,7 @@ static int do_test_single(struct bpf_align_test *test)
|
||||
break;
|
||||
}
|
||||
}
|
||||
out:
|
||||
if (fd_prog >= 0)
|
||||
close(fd_prog);
|
||||
}
|
||||
|
||||
@@ -162,6 +162,66 @@ static void test_uaf(struct arena_atomics *skel)
|
||||
ASSERT_EQ(skel->arena->uaf_recovery_fails, 0, "uaf_recovery_fails");
|
||||
}
|
||||
|
||||
static void test_load_acquire(struct arena_atomics *skel)
|
||||
{
|
||||
LIBBPF_OPTS(bpf_test_run_opts, topts);
|
||||
int err, prog_fd;
|
||||
|
||||
if (skel->data->skip_lacq_srel_tests) {
|
||||
printf("%s:SKIP: ENABLE_ATOMICS_TESTS not defined, Clang doesn't support addr_space_cast, and/or JIT doesn't support load-acquire\n",
|
||||
__func__);
|
||||
test__skip();
|
||||
return;
|
||||
}
|
||||
|
||||
/* No need to attach it, just run it directly */
|
||||
prog_fd = bpf_program__fd(skel->progs.load_acquire);
|
||||
err = bpf_prog_test_run_opts(prog_fd, &topts);
|
||||
if (!ASSERT_OK(err, "test_run_opts err"))
|
||||
return;
|
||||
if (!ASSERT_OK(topts.retval, "test_run_opts retval"))
|
||||
return;
|
||||
|
||||
ASSERT_EQ(skel->arena->load_acquire8_result, 0x12,
|
||||
"load_acquire8_result");
|
||||
ASSERT_EQ(skel->arena->load_acquire16_result, 0x1234,
|
||||
"load_acquire16_result");
|
||||
ASSERT_EQ(skel->arena->load_acquire32_result, 0x12345678,
|
||||
"load_acquire32_result");
|
||||
ASSERT_EQ(skel->arena->load_acquire64_result, 0x1234567890abcdef,
|
||||
"load_acquire64_result");
|
||||
}
|
||||
|
||||
static void test_store_release(struct arena_atomics *skel)
|
||||
{
|
||||
LIBBPF_OPTS(bpf_test_run_opts, topts);
|
||||
int err, prog_fd;
|
||||
|
||||
if (skel->data->skip_lacq_srel_tests) {
|
||||
printf("%s:SKIP: ENABLE_ATOMICS_TESTS not defined, Clang doesn't support addr_space_cast, and/or JIT doesn't support store-release\n",
|
||||
__func__);
|
||||
test__skip();
|
||||
return;
|
||||
}
|
||||
|
||||
/* No need to attach it, just run it directly */
|
||||
prog_fd = bpf_program__fd(skel->progs.store_release);
|
||||
err = bpf_prog_test_run_opts(prog_fd, &topts);
|
||||
if (!ASSERT_OK(err, "test_run_opts err"))
|
||||
return;
|
||||
if (!ASSERT_OK(topts.retval, "test_run_opts retval"))
|
||||
return;
|
||||
|
||||
ASSERT_EQ(skel->arena->store_release8_result, 0x12,
|
||||
"store_release8_result");
|
||||
ASSERT_EQ(skel->arena->store_release16_result, 0x1234,
|
||||
"store_release16_result");
|
||||
ASSERT_EQ(skel->arena->store_release32_result, 0x12345678,
|
||||
"store_release32_result");
|
||||
ASSERT_EQ(skel->arena->store_release64_result, 0x1234567890abcdef,
|
||||
"store_release64_result");
|
||||
}
|
||||
|
||||
void test_arena_atomics(void)
|
||||
{
|
||||
struct arena_atomics *skel;
|
||||
@@ -171,7 +231,7 @@ void test_arena_atomics(void)
|
||||
if (!ASSERT_OK_PTR(skel, "arena atomics skeleton open"))
|
||||
return;
|
||||
|
||||
if (skel->data->skip_tests) {
|
||||
if (skel->data->skip_all_tests) {
|
||||
printf("%s:SKIP:no ENABLE_ATOMICS_TESTS or no addr_space_cast support in clang",
|
||||
__func__);
|
||||
test__skip();
|
||||
@@ -198,6 +258,10 @@ void test_arena_atomics(void)
|
||||
test_xchg(skel);
|
||||
if (test__start_subtest("uaf"))
|
||||
test_uaf(skel);
|
||||
if (test__start_subtest("load_acquire"))
|
||||
test_load_acquire(skel);
|
||||
if (test__start_subtest("store_release"))
|
||||
test_store_release(skel);
|
||||
|
||||
cleanup:
|
||||
arena_atomics__destroy(skel);
|
||||
|
||||
108
tools/testing/selftests/bpf/prog_tests/arena_spin_lock.c
Normal file
108
tools/testing/selftests/bpf/prog_tests/arena_spin_lock.c
Normal file
@@ -0,0 +1,108 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */
|
||||
#include <test_progs.h>
|
||||
#include <network_helpers.h>
|
||||
#include <sys/sysinfo.h>
|
||||
|
||||
struct __qspinlock { int val; };
|
||||
typedef struct __qspinlock arena_spinlock_t;
|
||||
|
||||
struct arena_qnode {
|
||||
unsigned long next;
|
||||
int count;
|
||||
int locked;
|
||||
};
|
||||
|
||||
#include "arena_spin_lock.skel.h"
|
||||
|
||||
static long cpu;
|
||||
static int repeat;
|
||||
|
||||
pthread_barrier_t barrier;
|
||||
|
||||
static void *spin_lock_thread(void *arg)
|
||||
{
|
||||
int err, prog_fd = *(u32 *)arg;
|
||||
LIBBPF_OPTS(bpf_test_run_opts, topts,
|
||||
.data_in = &pkt_v4,
|
||||
.data_size_in = sizeof(pkt_v4),
|
||||
.repeat = repeat,
|
||||
);
|
||||
cpu_set_t cpuset;
|
||||
|
||||
CPU_ZERO(&cpuset);
|
||||
CPU_SET(__sync_fetch_and_add(&cpu, 1), &cpuset);
|
||||
ASSERT_OK(pthread_setaffinity_np(pthread_self(), sizeof(cpuset), &cpuset), "cpu affinity");
|
||||
|
||||
err = pthread_barrier_wait(&barrier);
|
||||
if (err != PTHREAD_BARRIER_SERIAL_THREAD && err != 0)
|
||||
ASSERT_FALSE(true, "pthread_barrier");
|
||||
|
||||
err = bpf_prog_test_run_opts(prog_fd, &topts);
|
||||
ASSERT_OK(err, "test_run err");
|
||||
ASSERT_EQ((int)topts.retval, 0, "test_run retval");
|
||||
|
||||
pthread_exit(arg);
|
||||
}
|
||||
|
||||
static void test_arena_spin_lock_size(int size)
|
||||
{
|
||||
LIBBPF_OPTS(bpf_test_run_opts, topts);
|
||||
struct arena_spin_lock *skel;
|
||||
pthread_t thread_id[16];
|
||||
int prog_fd, i, err;
|
||||
void *ret;
|
||||
|
||||
if (get_nprocs() < 2) {
|
||||
test__skip();
|
||||
return;
|
||||
}
|
||||
|
||||
skel = arena_spin_lock__open_and_load();
|
||||
if (!ASSERT_OK_PTR(skel, "arena_spin_lock__open_and_load"))
|
||||
return;
|
||||
if (skel->data->test_skip == 2) {
|
||||
test__skip();
|
||||
goto end;
|
||||
}
|
||||
skel->bss->cs_count = size;
|
||||
skel->bss->limit = repeat * 16;
|
||||
|
||||
ASSERT_OK(pthread_barrier_init(&barrier, NULL, 16), "barrier init");
|
||||
|
||||
prog_fd = bpf_program__fd(skel->progs.prog);
|
||||
for (i = 0; i < 16; i++) {
|
||||
err = pthread_create(&thread_id[i], NULL, &spin_lock_thread, &prog_fd);
|
||||
if (!ASSERT_OK(err, "pthread_create"))
|
||||
goto end_barrier;
|
||||
}
|
||||
|
||||
for (i = 0; i < 16; i++) {
|
||||
if (!ASSERT_OK(pthread_join(thread_id[i], &ret), "pthread_join"))
|
||||
goto end_barrier;
|
||||
if (!ASSERT_EQ(ret, &prog_fd, "ret == prog_fd"))
|
||||
goto end_barrier;
|
||||
}
|
||||
|
||||
ASSERT_EQ(skel->bss->counter, repeat * 16, "check counter value");
|
||||
|
||||
end_barrier:
|
||||
pthread_barrier_destroy(&barrier);
|
||||
end:
|
||||
arena_spin_lock__destroy(skel);
|
||||
return;
|
||||
}
|
||||
|
||||
void test_arena_spin_lock(void)
|
||||
{
|
||||
repeat = 1000;
|
||||
if (test__start_subtest("arena_spin_lock_1"))
|
||||
test_arena_spin_lock_size(1);
|
||||
cpu = 0;
|
||||
if (test__start_subtest("arena_spin_lock_1000"))
|
||||
test_arena_spin_lock_size(1000);
|
||||
cpu = 0;
|
||||
repeat = 100;
|
||||
if (test__start_subtest("arena_spin_lock_50000"))
|
||||
test_arena_spin_lock_size(50000);
|
||||
}
|
||||
@@ -6,6 +6,10 @@
|
||||
#include <test_progs.h>
|
||||
#include "bloom_filter_map.skel.h"
|
||||
|
||||
#ifndef NUMA_NO_NODE
|
||||
#define NUMA_NO_NODE (-1)
|
||||
#endif
|
||||
|
||||
static void test_fail_cases(void)
|
||||
{
|
||||
LIBBPF_OPTS(bpf_map_create_opts, opts);
|
||||
@@ -69,6 +73,7 @@ static void test_success_cases(void)
|
||||
|
||||
/* Create a map */
|
||||
opts.map_flags = BPF_F_ZERO_SEED | BPF_F_NUMA_NODE;
|
||||
opts.numa_node = NUMA_NO_NODE;
|
||||
fd = bpf_map_create(BPF_MAP_TYPE_BLOOM_FILTER, NULL, 0, sizeof(value), 100, &opts);
|
||||
if (!ASSERT_GE(fd, 0, "bpf_map_create bloom filter success case"))
|
||||
return;
|
||||
|
||||
@@ -323,19 +323,87 @@ static void test_task_pidfd(void)
|
||||
static void test_task_sleepable(void)
|
||||
{
|
||||
struct bpf_iter_tasks *skel;
|
||||
int pid, status, err, data_pipe[2], finish_pipe[2], c;
|
||||
char *test_data = NULL;
|
||||
char *test_data_long = NULL;
|
||||
char *data[2];
|
||||
|
||||
if (!ASSERT_OK(pipe(data_pipe), "data_pipe") ||
|
||||
!ASSERT_OK(pipe(finish_pipe), "finish_pipe"))
|
||||
return;
|
||||
|
||||
skel = bpf_iter_tasks__open_and_load();
|
||||
if (!ASSERT_OK_PTR(skel, "bpf_iter_tasks__open_and_load"))
|
||||
return;
|
||||
|
||||
pid = fork();
|
||||
if (!ASSERT_GE(pid, 0, "fork"))
|
||||
return;
|
||||
|
||||
if (pid == 0) {
|
||||
/* child */
|
||||
close(data_pipe[0]);
|
||||
close(finish_pipe[1]);
|
||||
|
||||
test_data = malloc(sizeof(char) * 10);
|
||||
strncpy(test_data, "test_data", 10);
|
||||
test_data[9] = '\0';
|
||||
|
||||
test_data_long = malloc(sizeof(char) * 5000);
|
||||
for (int i = 0; i < 5000; ++i) {
|
||||
if (i % 2 == 0)
|
||||
test_data_long[i] = 'b';
|
||||
else
|
||||
test_data_long[i] = 'a';
|
||||
}
|
||||
test_data_long[4999] = '\0';
|
||||
|
||||
data[0] = test_data;
|
||||
data[1] = test_data_long;
|
||||
|
||||
write(data_pipe[1], &data, sizeof(data));
|
||||
|
||||
/* keep child alive until after the test */
|
||||
err = read(finish_pipe[0], &c, 1);
|
||||
if (err != 1)
|
||||
exit(-1);
|
||||
|
||||
close(data_pipe[1]);
|
||||
close(finish_pipe[0]);
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
/* parent */
|
||||
close(data_pipe[1]);
|
||||
close(finish_pipe[0]);
|
||||
|
||||
err = read(data_pipe[0], &data, sizeof(data));
|
||||
ASSERT_EQ(err, sizeof(data), "read_check");
|
||||
|
||||
skel->bss->user_ptr = data[0];
|
||||
skel->bss->user_ptr_long = data[1];
|
||||
skel->bss->pid = pid;
|
||||
|
||||
do_dummy_read(skel->progs.dump_task_sleepable);
|
||||
|
||||
ASSERT_GT(skel->bss->num_expected_failure_copy_from_user_task, 0,
|
||||
"num_expected_failure_copy_from_user_task");
|
||||
ASSERT_GT(skel->bss->num_success_copy_from_user_task, 0,
|
||||
"num_success_copy_from_user_task");
|
||||
ASSERT_GT(skel->bss->num_expected_failure_copy_from_user_task_str, 0,
|
||||
"num_expected_failure_copy_from_user_task_str");
|
||||
ASSERT_GT(skel->bss->num_success_copy_from_user_task_str, 0,
|
||||
"num_success_copy_from_user_task_str");
|
||||
|
||||
bpf_iter_tasks__destroy(skel);
|
||||
|
||||
write(finish_pipe[1], &c, 1);
|
||||
err = waitpid(pid, &status, 0);
|
||||
ASSERT_EQ(err, pid, "waitpid");
|
||||
ASSERT_EQ(status, 0, "zero_child_exit");
|
||||
|
||||
close(data_pipe[0]);
|
||||
close(finish_pipe[1]);
|
||||
}
|
||||
|
||||
static void test_task_stack(void)
|
||||
|
||||
@@ -72,11 +72,14 @@ static void test_bpf_nf_ct(int mode)
|
||||
if (!ASSERT_OK(system(cmd), cmd))
|
||||
goto end;
|
||||
|
||||
srv_port = (mode == TEST_XDP) ? 5005 : 5006;
|
||||
srv_fd = start_server(AF_INET, SOCK_STREAM, "127.0.0.1", srv_port, TIMEOUT_MS);
|
||||
srv_fd = start_server(AF_INET, SOCK_STREAM, "127.0.0.1", 0, TIMEOUT_MS);
|
||||
if (!ASSERT_GE(srv_fd, 0, "start_server"))
|
||||
goto end;
|
||||
|
||||
srv_port = get_socket_local_port(srv_fd);
|
||||
if (!ASSERT_GE(srv_port, 0, "get_sock_local_port"))
|
||||
goto end;
|
||||
|
||||
client_fd = connect_to_server(srv_fd);
|
||||
if (!ASSERT_GE(client_fd, 0, "connect_to_server"))
|
||||
goto end;
|
||||
@@ -91,7 +94,7 @@ static void test_bpf_nf_ct(int mode)
|
||||
skel->bss->saddr = peer_addr.sin_addr.s_addr;
|
||||
skel->bss->sport = peer_addr.sin_port;
|
||||
skel->bss->daddr = peer_addr.sin_addr.s_addr;
|
||||
skel->bss->dport = htons(srv_port);
|
||||
skel->bss->dport = srv_port;
|
||||
|
||||
if (mode == TEST_XDP)
|
||||
prog_fd = bpf_program__fd(skel->progs.nf_xdp_ct_test);
|
||||
|
||||
@@ -3866,11 +3866,11 @@ static struct btf_raw_test raw_tests[] = {
|
||||
.err_str = "vlen != 0",
|
||||
},
|
||||
{
|
||||
.descr = "decl_tag test #8, invalid kflag",
|
||||
.descr = "decl_tag test #8, tag with kflag",
|
||||
.raw_types = {
|
||||
BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [1] */
|
||||
BTF_VAR_ENC(NAME_TBD, 1, 0), /* [2] */
|
||||
BTF_TYPE_ENC(NAME_TBD, BTF_INFO_ENC(BTF_KIND_DECL_TAG, 1, 0), 2), (-1),
|
||||
BTF_DECL_ATTR_ENC(NAME_TBD, 2, -1),
|
||||
BTF_END_RAW,
|
||||
},
|
||||
BTF_STR_SEC("\0local\0tag1"),
|
||||
@@ -3881,8 +3881,6 @@ static struct btf_raw_test raw_tests[] = {
|
||||
.key_type_id = 1,
|
||||
.value_type_id = 1,
|
||||
.max_entries = 1,
|
||||
.btf_load_err = true,
|
||||
.err_str = "Invalid btf_info kind_flag",
|
||||
},
|
||||
{
|
||||
.descr = "decl_tag test #9, var, invalid component_idx",
|
||||
@@ -4206,6 +4204,23 @@ static struct btf_raw_test raw_tests[] = {
|
||||
.btf_load_err = true,
|
||||
.err_str = "Type tags don't precede modifiers",
|
||||
},
|
||||
{
|
||||
.descr = "type_tag test #7, tag with kflag",
|
||||
.raw_types = {
|
||||
BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [1] */
|
||||
BTF_TYPE_ATTR_ENC(NAME_TBD, 1), /* [2] */
|
||||
BTF_PTR_ENC(2), /* [3] */
|
||||
BTF_END_RAW,
|
||||
},
|
||||
BTF_STR_SEC("\0tag"),
|
||||
.map_type = BPF_MAP_TYPE_ARRAY,
|
||||
.map_name = "tag_type_check_btf",
|
||||
.key_size = sizeof(int),
|
||||
.value_size = 4,
|
||||
.key_type_id = 1,
|
||||
.value_type_id = 1,
|
||||
.max_entries = 1,
|
||||
},
|
||||
{
|
||||
.descr = "enum64 test #1, unsigned, size 8",
|
||||
.raw_types = {
|
||||
|
||||
@@ -126,25 +126,68 @@ done:
|
||||
return err;
|
||||
}
|
||||
|
||||
static char *dump_buf;
|
||||
static size_t dump_buf_sz;
|
||||
static FILE *dump_buf_file;
|
||||
struct test_ctx {
|
||||
struct btf *btf;
|
||||
struct btf_dump *d;
|
||||
char *dump_buf;
|
||||
size_t dump_buf_sz;
|
||||
FILE *dump_buf_file;
|
||||
};
|
||||
|
||||
static void test_ctx__free(struct test_ctx *t)
|
||||
{
|
||||
fclose(t->dump_buf_file);
|
||||
free(t->dump_buf);
|
||||
btf_dump__free(t->d);
|
||||
btf__free(t->btf);
|
||||
}
|
||||
|
||||
static int test_ctx__init(struct test_ctx *t)
|
||||
{
|
||||
t->dump_buf_file = open_memstream(&t->dump_buf, &t->dump_buf_sz);
|
||||
if (!ASSERT_OK_PTR(t->dump_buf_file, "dump_memstream"))
|
||||
return -1;
|
||||
t->btf = btf__new_empty();
|
||||
if (!ASSERT_OK_PTR(t->btf, "new_empty"))
|
||||
goto err_out;
|
||||
t->d = btf_dump__new(t->btf, btf_dump_printf, t->dump_buf_file, NULL);
|
||||
if (!ASSERT_OK(libbpf_get_error(t->d), "btf_dump__new"))
|
||||
goto err_out;
|
||||
|
||||
return 0;
|
||||
|
||||
err_out:
|
||||
test_ctx__free(t);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void test_ctx__dump_and_compare(struct test_ctx *t,
|
||||
const char *expected_output,
|
||||
const char *message)
|
||||
{
|
||||
int i, err;
|
||||
|
||||
for (i = 1; i < btf__type_cnt(t->btf); i++) {
|
||||
err = btf_dump__dump_type(t->d, i);
|
||||
ASSERT_OK(err, "dump_type_ok");
|
||||
}
|
||||
|
||||
fflush(t->dump_buf_file);
|
||||
t->dump_buf[t->dump_buf_sz] = 0; /* some libc implementations don't do this */
|
||||
|
||||
ASSERT_STREQ(t->dump_buf, expected_output, message);
|
||||
}
|
||||
|
||||
static void test_btf_dump_incremental(void)
|
||||
{
|
||||
struct btf *btf = NULL;
|
||||
struct btf_dump *d = NULL;
|
||||
int id, err, i;
|
||||
struct test_ctx t = {};
|
||||
struct btf *btf;
|
||||
int id, err;
|
||||
|
||||
dump_buf_file = open_memstream(&dump_buf, &dump_buf_sz);
|
||||
if (!ASSERT_OK_PTR(dump_buf_file, "dump_memstream"))
|
||||
if (test_ctx__init(&t))
|
||||
return;
|
||||
btf = btf__new_empty();
|
||||
if (!ASSERT_OK_PTR(btf, "new_empty"))
|
||||
goto err_out;
|
||||
d = btf_dump__new(btf, btf_dump_printf, dump_buf_file, NULL);
|
||||
if (!ASSERT_OK(libbpf_get_error(d), "btf_dump__new"))
|
||||
goto err_out;
|
||||
|
||||
btf = t.btf;
|
||||
|
||||
/* First, generate BTF corresponding to the following C code:
|
||||
*
|
||||
@@ -182,15 +225,7 @@ static void test_btf_dump_incremental(void)
|
||||
err = btf__add_field(btf, "x", 4, 0, 0);
|
||||
ASSERT_OK(err, "field_ok");
|
||||
|
||||
for (i = 1; i < btf__type_cnt(btf); i++) {
|
||||
err = btf_dump__dump_type(d, i);
|
||||
ASSERT_OK(err, "dump_type_ok");
|
||||
}
|
||||
|
||||
fflush(dump_buf_file);
|
||||
dump_buf[dump_buf_sz] = 0; /* some libc implementations don't do this */
|
||||
|
||||
ASSERT_STREQ(dump_buf,
|
||||
test_ctx__dump_and_compare(&t,
|
||||
"enum x;\n"
|
||||
"\n"
|
||||
"enum x {\n"
|
||||
@@ -221,7 +256,7 @@ static void test_btf_dump_incremental(void)
|
||||
* enum values don't conflict;
|
||||
*
|
||||
*/
|
||||
fseek(dump_buf_file, 0, SEEK_SET);
|
||||
fseek(t.dump_buf_file, 0, SEEK_SET);
|
||||
|
||||
id = btf__add_struct(btf, "s", 4);
|
||||
ASSERT_EQ(id, 7, "struct_id");
|
||||
@@ -232,14 +267,7 @@ static void test_btf_dump_incremental(void)
|
||||
err = btf__add_field(btf, "s", 6, 64, 0);
|
||||
ASSERT_OK(err, "field_ok");
|
||||
|
||||
for (i = 1; i < btf__type_cnt(btf); i++) {
|
||||
err = btf_dump__dump_type(d, i);
|
||||
ASSERT_OK(err, "dump_type_ok");
|
||||
}
|
||||
|
||||
fflush(dump_buf_file);
|
||||
dump_buf[dump_buf_sz] = 0; /* some libc implementations don't do this */
|
||||
ASSERT_STREQ(dump_buf,
|
||||
test_ctx__dump_and_compare(&t,
|
||||
"struct s___2 {\n"
|
||||
" enum x x;\n"
|
||||
" enum {\n"
|
||||
@@ -248,11 +276,53 @@ static void test_btf_dump_incremental(void)
|
||||
" struct s s;\n"
|
||||
"};\n\n" , "c_dump1");
|
||||
|
||||
err_out:
|
||||
fclose(dump_buf_file);
|
||||
free(dump_buf);
|
||||
btf_dump__free(d);
|
||||
btf__free(btf);
|
||||
test_ctx__free(&t);
|
||||
}
|
||||
|
||||
static void test_btf_dump_type_tags(void)
|
||||
{
|
||||
struct test_ctx t = {};
|
||||
struct btf *btf;
|
||||
int id, err;
|
||||
|
||||
if (test_ctx__init(&t))
|
||||
return;
|
||||
|
||||
btf = t.btf;
|
||||
|
||||
/* Generate BTF corresponding to the following C code:
|
||||
*
|
||||
* struct s {
|
||||
* void __attribute__((btf_type_tag(\"void_tag\"))) *p1;
|
||||
* void __attribute__((void_attr)) *p2;
|
||||
* };
|
||||
*
|
||||
*/
|
||||
|
||||
id = btf__add_type_tag(btf, "void_tag", 0);
|
||||
ASSERT_EQ(id, 1, "type_tag_id");
|
||||
id = btf__add_ptr(btf, id);
|
||||
ASSERT_EQ(id, 2, "void_ptr_id1");
|
||||
|
||||
id = btf__add_type_attr(btf, "void_attr", 0);
|
||||
ASSERT_EQ(id, 3, "type_attr_id");
|
||||
id = btf__add_ptr(btf, id);
|
||||
ASSERT_EQ(id, 4, "void_ptr_id2");
|
||||
|
||||
id = btf__add_struct(btf, "s", 8);
|
||||
ASSERT_EQ(id, 5, "struct_id");
|
||||
err = btf__add_field(btf, "p1", 2, 0, 0);
|
||||
ASSERT_OK(err, "field_ok1");
|
||||
err = btf__add_field(btf, "p2", 4, 0, 0);
|
||||
ASSERT_OK(err, "field_ok2");
|
||||
|
||||
test_ctx__dump_and_compare(&t,
|
||||
"struct s {\n"
|
||||
" void __attribute__((btf_type_tag(\"void_tag\"))) *p1;\n"
|
||||
" void __attribute__((void_attr)) *p2;\n"
|
||||
"};\n\n", "dump_and_compare");
|
||||
|
||||
test_ctx__free(&t);
|
||||
}
|
||||
|
||||
#define STRSIZE 4096
|
||||
@@ -874,6 +944,9 @@ void test_btf_dump() {
|
||||
if (test__start_subtest("btf_dump: incremental"))
|
||||
test_btf_dump_incremental();
|
||||
|
||||
if (test__start_subtest("btf_dump: type_tags"))
|
||||
test_btf_dump_type_tags();
|
||||
|
||||
btf = libbpf_find_kernel_btf();
|
||||
if (!ASSERT_OK_PTR(btf, "no kernel BTF found"))
|
||||
return;
|
||||
|
||||
128
tools/testing/selftests/bpf/prog_tests/cgroup_preorder.c
Normal file
128
tools/testing/selftests/bpf/prog_tests/cgroup_preorder.c
Normal file
@@ -0,0 +1,128 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */
|
||||
#include <test_progs.h>
|
||||
#include "cgroup_helpers.h"
|
||||
#include "cgroup_preorder.skel.h"
|
||||
|
||||
static int run_getsockopt_test(int cg_parent, int cg_child, int sock_fd, bool all_preorder)
|
||||
{
|
||||
LIBBPF_OPTS(bpf_prog_attach_opts, opts);
|
||||
enum bpf_attach_type prog_c_atype, prog_c2_atype, prog_p_atype, prog_p2_atype;
|
||||
int prog_c_fd, prog_c2_fd, prog_p_fd, prog_p2_fd;
|
||||
struct cgroup_preorder *skel = NULL;
|
||||
struct bpf_program *prog;
|
||||
__u8 *result, buf;
|
||||
socklen_t optlen;
|
||||
int err = 0;
|
||||
|
||||
skel = cgroup_preorder__open_and_load();
|
||||
if (!ASSERT_OK_PTR(skel, "cgroup_preorder__open_and_load"))
|
||||
return 0;
|
||||
|
||||
buf = 0x00;
|
||||
err = setsockopt(sock_fd, SOL_IP, IP_TOS, &buf, 1);
|
||||
if (!ASSERT_OK(err, "setsockopt"))
|
||||
goto close_skel;
|
||||
|
||||
opts.flags = BPF_F_ALLOW_MULTI;
|
||||
if (all_preorder)
|
||||
opts.flags |= BPF_F_PREORDER;
|
||||
prog = skel->progs.child;
|
||||
prog_c_fd = bpf_program__fd(prog);
|
||||
prog_c_atype = bpf_program__expected_attach_type(prog);
|
||||
err = bpf_prog_attach_opts(prog_c_fd, cg_child, prog_c_atype, &opts);
|
||||
if (!ASSERT_OK(err, "bpf_prog_attach_opts-child"))
|
||||
goto close_skel;
|
||||
|
||||
opts.flags = BPF_F_ALLOW_MULTI | BPF_F_PREORDER;
|
||||
prog = skel->progs.child_2;
|
||||
prog_c2_fd = bpf_program__fd(prog);
|
||||
prog_c2_atype = bpf_program__expected_attach_type(prog);
|
||||
err = bpf_prog_attach_opts(prog_c2_fd, cg_child, prog_c2_atype, &opts);
|
||||
if (!ASSERT_OK(err, "bpf_prog_attach_opts-child_2"))
|
||||
goto detach_child;
|
||||
|
||||
optlen = 1;
|
||||
err = getsockopt(sock_fd, SOL_IP, IP_TOS, &buf, &optlen);
|
||||
if (!ASSERT_OK(err, "getsockopt"))
|
||||
goto detach_child_2;
|
||||
|
||||
result = skel->bss->result;
|
||||
if (all_preorder)
|
||||
ASSERT_TRUE(result[0] == 1 && result[1] == 2, "child only");
|
||||
else
|
||||
ASSERT_TRUE(result[0] == 2 && result[1] == 1, "child only");
|
||||
|
||||
skel->bss->idx = 0;
|
||||
memset(result, 0, 4);
|
||||
|
||||
opts.flags = BPF_F_ALLOW_MULTI;
|
||||
if (all_preorder)
|
||||
opts.flags |= BPF_F_PREORDER;
|
||||
prog = skel->progs.parent;
|
||||
prog_p_fd = bpf_program__fd(prog);
|
||||
prog_p_atype = bpf_program__expected_attach_type(prog);
|
||||
err = bpf_prog_attach_opts(prog_p_fd, cg_parent, prog_p_atype, &opts);
|
||||
if (!ASSERT_OK(err, "bpf_prog_attach_opts-parent"))
|
||||
goto detach_child_2;
|
||||
|
||||
opts.flags = BPF_F_ALLOW_MULTI | BPF_F_PREORDER;
|
||||
prog = skel->progs.parent_2;
|
||||
prog_p2_fd = bpf_program__fd(prog);
|
||||
prog_p2_atype = bpf_program__expected_attach_type(prog);
|
||||
err = bpf_prog_attach_opts(prog_p2_fd, cg_parent, prog_p2_atype, &opts);
|
||||
if (!ASSERT_OK(err, "bpf_prog_attach_opts-parent_2"))
|
||||
goto detach_parent;
|
||||
|
||||
err = getsockopt(sock_fd, SOL_IP, IP_TOS, &buf, &optlen);
|
||||
if (!ASSERT_OK(err, "getsockopt"))
|
||||
goto detach_parent_2;
|
||||
|
||||
if (all_preorder)
|
||||
ASSERT_TRUE(result[0] == 3 && result[1] == 4 && result[2] == 1 && result[3] == 2,
|
||||
"parent and child");
|
||||
else
|
||||
ASSERT_TRUE(result[0] == 4 && result[1] == 2 && result[2] == 1 && result[3] == 3,
|
||||
"parent and child");
|
||||
|
||||
detach_parent_2:
|
||||
ASSERT_OK(bpf_prog_detach2(prog_p2_fd, cg_parent, prog_p2_atype),
|
||||
"bpf_prog_detach2-parent_2");
|
||||
detach_parent:
|
||||
ASSERT_OK(bpf_prog_detach2(prog_p_fd, cg_parent, prog_p_atype),
|
||||
"bpf_prog_detach2-parent");
|
||||
detach_child_2:
|
||||
ASSERT_OK(bpf_prog_detach2(prog_c2_fd, cg_child, prog_c2_atype),
|
||||
"bpf_prog_detach2-child_2");
|
||||
detach_child:
|
||||
ASSERT_OK(bpf_prog_detach2(prog_c_fd, cg_child, prog_c_atype),
|
||||
"bpf_prog_detach2-child");
|
||||
close_skel:
|
||||
cgroup_preorder__destroy(skel);
|
||||
return err;
|
||||
}
|
||||
|
||||
void test_cgroup_preorder(void)
|
||||
{
|
||||
int cg_parent = -1, cg_child = -1, sock_fd = -1;
|
||||
|
||||
cg_parent = test__join_cgroup("/parent");
|
||||
if (!ASSERT_GE(cg_parent, 0, "join_cgroup /parent"))
|
||||
goto out;
|
||||
|
||||
cg_child = test__join_cgroup("/parent/child");
|
||||
if (!ASSERT_GE(cg_child, 0, "join_cgroup /parent/child"))
|
||||
goto out;
|
||||
|
||||
sock_fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (!ASSERT_GE(sock_fd, 0, "socket"))
|
||||
goto out;
|
||||
|
||||
ASSERT_OK(run_getsockopt_test(cg_parent, cg_child, sock_fd, false), "getsockopt_test_1");
|
||||
ASSERT_OK(run_getsockopt_test(cg_parent, cg_child, sock_fd, true), "getsockopt_test_2");
|
||||
|
||||
out:
|
||||
close(sock_fd);
|
||||
close(cg_child);
|
||||
close(cg_parent);
|
||||
}
|
||||
@@ -10,12 +10,18 @@
|
||||
static int run_test(int cgroup_fd, int server_fd, bool classid)
|
||||
{
|
||||
struct connect4_dropper *skel;
|
||||
int fd, err = 0;
|
||||
int fd, err = 0, port;
|
||||
|
||||
skel = connect4_dropper__open_and_load();
|
||||
if (!ASSERT_OK_PTR(skel, "skel_open"))
|
||||
return -1;
|
||||
|
||||
port = get_socket_local_port(server_fd);
|
||||
if (!ASSERT_GE(port, 0, "get_socket_local_port"))
|
||||
return -1;
|
||||
|
||||
skel->bss->port = ntohs(port);
|
||||
|
||||
skel->links.connect_v4_dropper =
|
||||
bpf_program__attach_cgroup(skel->progs.connect_v4_dropper,
|
||||
cgroup_fd);
|
||||
@@ -48,10 +54,9 @@ void test_cgroup_v1v2(void)
|
||||
{
|
||||
struct network_helper_opts opts = {};
|
||||
int server_fd, client_fd, cgroup_fd;
|
||||
static const int port = 60120;
|
||||
|
||||
/* Step 1: Check base connectivity works without any BPF. */
|
||||
server_fd = start_server(AF_INET, SOCK_STREAM, NULL, port, 0);
|
||||
server_fd = start_server(AF_INET, SOCK_STREAM, NULL, 0, 0);
|
||||
if (!ASSERT_GE(server_fd, 0, "server_fd"))
|
||||
return;
|
||||
client_fd = connect_to_fd_opts(server_fd, &opts);
|
||||
@@ -66,7 +71,7 @@ void test_cgroup_v1v2(void)
|
||||
cgroup_fd = test__join_cgroup("/connect_dropper");
|
||||
if (!ASSERT_GE(cgroup_fd, 0, "cgroup_fd"))
|
||||
return;
|
||||
server_fd = start_server(AF_INET, SOCK_STREAM, NULL, port, 0);
|
||||
server_fd = start_server(AF_INET, SOCK_STREAM, NULL, 0, 0);
|
||||
if (!ASSERT_GE(server_fd, 0, "server_fd")) {
|
||||
close(cgroup_fd);
|
||||
return;
|
||||
|
||||
@@ -1,107 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include "bpf/libbpf.h"
|
||||
#include "changes_pkt_data_freplace.skel.h"
|
||||
#include "changes_pkt_data.skel.h"
|
||||
#include <test_progs.h>
|
||||
|
||||
static void print_verifier_log(const char *log)
|
||||
{
|
||||
if (env.verbosity >= VERBOSE_VERY)
|
||||
fprintf(stdout, "VERIFIER LOG:\n=============\n%s=============\n", log);
|
||||
}
|
||||
|
||||
static void test_aux(const char *main_prog_name,
|
||||
const char *to_be_replaced,
|
||||
const char *replacement,
|
||||
bool expect_load)
|
||||
{
|
||||
struct changes_pkt_data_freplace *freplace = NULL;
|
||||
struct bpf_program *freplace_prog = NULL;
|
||||
struct bpf_program *main_prog = NULL;
|
||||
LIBBPF_OPTS(bpf_object_open_opts, opts);
|
||||
struct changes_pkt_data *main = NULL;
|
||||
char log[16*1024];
|
||||
int err;
|
||||
|
||||
opts.kernel_log_buf = log;
|
||||
opts.kernel_log_size = sizeof(log);
|
||||
if (env.verbosity >= VERBOSE_SUPER)
|
||||
opts.kernel_log_level = 1 | 2 | 4;
|
||||
main = changes_pkt_data__open_opts(&opts);
|
||||
if (!ASSERT_OK_PTR(main, "changes_pkt_data__open"))
|
||||
goto out;
|
||||
main_prog = bpf_object__find_program_by_name(main->obj, main_prog_name);
|
||||
if (!ASSERT_OK_PTR(main_prog, "main_prog"))
|
||||
goto out;
|
||||
bpf_program__set_autoload(main_prog, true);
|
||||
err = changes_pkt_data__load(main);
|
||||
print_verifier_log(log);
|
||||
if (!ASSERT_OK(err, "changes_pkt_data__load"))
|
||||
goto out;
|
||||
freplace = changes_pkt_data_freplace__open_opts(&opts);
|
||||
if (!ASSERT_OK_PTR(freplace, "changes_pkt_data_freplace__open"))
|
||||
goto out;
|
||||
freplace_prog = bpf_object__find_program_by_name(freplace->obj, replacement);
|
||||
if (!ASSERT_OK_PTR(freplace_prog, "freplace_prog"))
|
||||
goto out;
|
||||
bpf_program__set_autoload(freplace_prog, true);
|
||||
bpf_program__set_autoattach(freplace_prog, true);
|
||||
bpf_program__set_attach_target(freplace_prog,
|
||||
bpf_program__fd(main_prog),
|
||||
to_be_replaced);
|
||||
err = changes_pkt_data_freplace__load(freplace);
|
||||
print_verifier_log(log);
|
||||
if (expect_load) {
|
||||
ASSERT_OK(err, "changes_pkt_data_freplace__load");
|
||||
} else {
|
||||
ASSERT_ERR(err, "changes_pkt_data_freplace__load");
|
||||
ASSERT_HAS_SUBSTR(log, "Extension program changes packet data", "error log");
|
||||
}
|
||||
|
||||
out:
|
||||
changes_pkt_data_freplace__destroy(freplace);
|
||||
changes_pkt_data__destroy(main);
|
||||
}
|
||||
|
||||
/* There are two global subprograms in both changes_pkt_data.skel.h:
|
||||
* - one changes packet data;
|
||||
* - another does not.
|
||||
* It is ok to freplace subprograms that change packet data with those
|
||||
* that either do or do not. It is only ok to freplace subprograms
|
||||
* that do not change packet data with those that do not as well.
|
||||
* The below tests check outcomes for each combination of such freplace.
|
||||
* Also test a case when main subprogram itself is replaced and is a single
|
||||
* subprogram in a program.
|
||||
*/
|
||||
void test_changes_pkt_data_freplace(void)
|
||||
{
|
||||
struct {
|
||||
const char *main;
|
||||
const char *to_be_replaced;
|
||||
bool changes;
|
||||
} mains[] = {
|
||||
{ "main_with_subprogs", "changes_pkt_data", true },
|
||||
{ "main_with_subprogs", "does_not_change_pkt_data", false },
|
||||
{ "main_changes", "main_changes", true },
|
||||
{ "main_does_not_change", "main_does_not_change", false },
|
||||
};
|
||||
struct {
|
||||
const char *func;
|
||||
bool changes;
|
||||
} replacements[] = {
|
||||
{ "changes_pkt_data", true },
|
||||
{ "does_not_change_pkt_data", false }
|
||||
};
|
||||
char buf[64];
|
||||
|
||||
for (int i = 0; i < ARRAY_SIZE(mains); ++i) {
|
||||
for (int j = 0; j < ARRAY_SIZE(replacements); ++j) {
|
||||
snprintf(buf, sizeof(buf), "%s_with_%s",
|
||||
mains[i].to_be_replaced, replacements[j].func);
|
||||
if (!test__start_subtest(buf))
|
||||
continue;
|
||||
test_aux(mains[i].main, mains[i].to_be_replaced, replacements[j].func,
|
||||
mains[i].changes || !replacements[j].changes);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include "compute_live_registers.skel.h"
|
||||
#include "test_progs.h"
|
||||
|
||||
void test_compute_live_registers(void)
|
||||
{
|
||||
RUN_TESTS(compute_live_registers);
|
||||
}
|
||||
@@ -85,11 +85,11 @@ static int duration = 0;
|
||||
#define NESTING_ERR_CASE(name) { \
|
||||
NESTING_CASE_COMMON(name), \
|
||||
.fails = true, \
|
||||
.run_btfgen_fails = true, \
|
||||
.run_btfgen_fails = true, \
|
||||
}
|
||||
|
||||
#define ARRAYS_DATA(struct_name) STRUCT_TO_CHAR_PTR(struct_name) { \
|
||||
.a = { [2] = 1 }, \
|
||||
.a = { [2] = 1, [3] = 11 }, \
|
||||
.b = { [1] = { [2] = { [3] = 2 } } }, \
|
||||
.c = { [1] = { .c = 3 } }, \
|
||||
.d = { [0] = { [0] = { .d = 4 } } }, \
|
||||
@@ -108,6 +108,7 @@ static int duration = 0;
|
||||
.input_len = sizeof(struct core_reloc_##name), \
|
||||
.output = STRUCT_TO_CHAR_PTR(core_reloc_arrays_output) { \
|
||||
.a2 = 1, \
|
||||
.a3 = 12, \
|
||||
.b123 = 2, \
|
||||
.c1c = 3, \
|
||||
.d00d = 4, \
|
||||
@@ -602,6 +603,7 @@ static const struct core_reloc_test_case test_cases[] = {
|
||||
ARRAYS_ERR_CASE(arrays___err_non_array),
|
||||
ARRAYS_ERR_CASE(arrays___err_wrong_val_type),
|
||||
ARRAYS_ERR_CASE(arrays___err_bad_zero_sz_arr),
|
||||
ARRAYS_ERR_CASE(arrays___err_bad_signed_arr_elem_sz),
|
||||
|
||||
/* enum/ptr/int handling scenarios */
|
||||
PRIMITIVES_CASE(primitives),
|
||||
|
||||
@@ -25,6 +25,10 @@ static const char * const cpumask_success_testcases[] = {
|
||||
"test_global_mask_nested_deep_rcu",
|
||||
"test_global_mask_nested_deep_array_rcu",
|
||||
"test_cpumask_weight",
|
||||
"test_refcount_null_tracking",
|
||||
"test_populate_reject_small_mask",
|
||||
"test_populate_reject_unaligned",
|
||||
"test_populate",
|
||||
};
|
||||
|
||||
static void verify_success(const char *prog_name)
|
||||
@@ -78,6 +82,5 @@ void test_cpumask(void)
|
||||
verify_success(cpumask_success_testcases[i]);
|
||||
}
|
||||
|
||||
RUN_TESTS(cpumask_success);
|
||||
RUN_TESTS(cpumask_failure);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ enum test_setup_type {
|
||||
SETUP_SYSCALL_SLEEP,
|
||||
SETUP_SKB_PROG,
|
||||
SETUP_SKB_PROG_TP,
|
||||
SETUP_XDP_PROG,
|
||||
};
|
||||
|
||||
static struct {
|
||||
@@ -18,6 +19,8 @@ static struct {
|
||||
} success_tests[] = {
|
||||
{"test_read_write", SETUP_SYSCALL_SLEEP},
|
||||
{"test_dynptr_data", SETUP_SYSCALL_SLEEP},
|
||||
{"test_dynptr_copy", SETUP_SYSCALL_SLEEP},
|
||||
{"test_dynptr_copy_xdp", SETUP_XDP_PROG},
|
||||
{"test_ringbuf", SETUP_SYSCALL_SLEEP},
|
||||
{"test_skb_readonly", SETUP_SKB_PROG},
|
||||
{"test_dynptr_skb_data", SETUP_SKB_PROG},
|
||||
@@ -120,6 +123,24 @@ static void verify_success(const char *prog_name, enum test_setup_type setup_typ
|
||||
|
||||
break;
|
||||
}
|
||||
case SETUP_XDP_PROG:
|
||||
{
|
||||
char data[5000];
|
||||
int err, prog_fd;
|
||||
LIBBPF_OPTS(bpf_test_run_opts, opts,
|
||||
.data_in = &data,
|
||||
.data_size_in = sizeof(data),
|
||||
.repeat = 1,
|
||||
);
|
||||
|
||||
prog_fd = bpf_program__fd(prog);
|
||||
err = bpf_prog_test_run_opts(prog_fd, &opts);
|
||||
|
||||
if (!ASSERT_OK(err, "test_run"))
|
||||
goto cleanup;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT_EQ(skel->bss->err, 0, "err");
|
||||
|
||||
@@ -83,8 +83,8 @@ static inline int bpf_prog_get_map_ids(int prog_fd, __u32 *nr_map_ids, __u32 *ma
|
||||
int err;
|
||||
|
||||
memset(&info, 0, len);
|
||||
info.nr_map_ids = *nr_map_ids,
|
||||
info.map_ids = ptr_to_u64(map_ids),
|
||||
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"))
|
||||
|
||||
9
tools/testing/selftests/bpf/prog_tests/fexit_noreturns.c
Normal file
9
tools/testing/selftests/bpf/prog_tests/fexit_noreturns.c
Normal file
@@ -0,0 +1,9 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include <test_progs.h>
|
||||
#include "fexit_noreturns.skel.h"
|
||||
|
||||
void test_fexit_noreturns(void)
|
||||
{
|
||||
RUN_TESTS(fexit_noreturns);
|
||||
}
|
||||
@@ -8,11 +8,12 @@
|
||||
#include <unistd.h>
|
||||
#include <test_progs.h>
|
||||
#include "test_get_xattr.skel.h"
|
||||
#include "test_set_remove_xattr.skel.h"
|
||||
#include "test_fsverity.skel.h"
|
||||
|
||||
static const char testfile[] = "/tmp/test_progs_fs_kfuncs";
|
||||
|
||||
static void test_xattr(void)
|
||||
static void test_get_xattr(const char *name, const char *value, bool allow_access)
|
||||
{
|
||||
struct test_get_xattr *skel = NULL;
|
||||
int fd = -1, err;
|
||||
@@ -25,7 +26,7 @@ static void test_xattr(void)
|
||||
close(fd);
|
||||
fd = -1;
|
||||
|
||||
err = setxattr(testfile, "user.kfuncs", "hello", sizeof("hello"), 0);
|
||||
err = setxattr(testfile, name, value, strlen(value) + 1, 0);
|
||||
if (err && errno == EOPNOTSUPP) {
|
||||
printf("%s:SKIP:local fs doesn't support xattr (%d)\n"
|
||||
"To run this test, make sure /tmp filesystem supports xattr.\n",
|
||||
@@ -48,16 +49,23 @@ static void test_xattr(void)
|
||||
goto out;
|
||||
|
||||
fd = open(testfile, O_RDONLY, 0644);
|
||||
|
||||
if (!ASSERT_GE(fd, 0, "open_file"))
|
||||
goto out;
|
||||
|
||||
ASSERT_EQ(skel->bss->found_xattr_from_file, 1, "found_xattr_from_file");
|
||||
|
||||
/* Trigger security_inode_getxattr */
|
||||
err = getxattr(testfile, "user.kfuncs", v, sizeof(v));
|
||||
ASSERT_EQ(err, -1, "getxattr_return");
|
||||
ASSERT_EQ(errno, EINVAL, "getxattr_errno");
|
||||
ASSERT_EQ(skel->bss->found_xattr_from_dentry, 1, "found_xattr_from_dentry");
|
||||
err = getxattr(testfile, name, v, sizeof(v));
|
||||
|
||||
if (allow_access) {
|
||||
ASSERT_EQ(err, -1, "getxattr_return");
|
||||
ASSERT_EQ(errno, EINVAL, "getxattr_errno");
|
||||
ASSERT_EQ(skel->bss->found_xattr_from_file, 1, "found_xattr_from_file");
|
||||
ASSERT_EQ(skel->bss->found_xattr_from_dentry, 1, "found_xattr_from_dentry");
|
||||
} else {
|
||||
ASSERT_EQ(err, strlen(value) + 1, "getxattr_return");
|
||||
ASSERT_EQ(skel->bss->found_xattr_from_file, 0, "found_xattr_from_file");
|
||||
ASSERT_EQ(skel->bss->found_xattr_from_dentry, 0, "found_xattr_from_dentry");
|
||||
}
|
||||
|
||||
out:
|
||||
close(fd);
|
||||
@@ -65,6 +73,127 @@ out:
|
||||
remove(testfile);
|
||||
}
|
||||
|
||||
/* xattr value we will set to security.bpf.foo */
|
||||
static const char value_foo[] = "hello";
|
||||
|
||||
static void read_and_validate_foo(struct test_set_remove_xattr *skel)
|
||||
{
|
||||
char value_out[32];
|
||||
int err;
|
||||
|
||||
err = getxattr(testfile, skel->rodata->xattr_foo, value_out, sizeof(value_out));
|
||||
ASSERT_EQ(err, sizeof(value_foo), "getxattr size foo");
|
||||
ASSERT_EQ(strncmp(value_out, value_foo, sizeof(value_foo)), 0, "strncmp value_foo");
|
||||
}
|
||||
|
||||
static void set_foo(struct test_set_remove_xattr *skel)
|
||||
{
|
||||
ASSERT_OK(setxattr(testfile, skel->rodata->xattr_foo, value_foo, strlen(value_foo) + 1, 0),
|
||||
"setxattr foo");
|
||||
}
|
||||
|
||||
static void validate_bar_match(struct test_set_remove_xattr *skel)
|
||||
{
|
||||
char value_out[32];
|
||||
int err;
|
||||
|
||||
err = getxattr(testfile, skel->rodata->xattr_bar, value_out, sizeof(value_out));
|
||||
ASSERT_EQ(err, sizeof(skel->data->value_bar), "getxattr size bar");
|
||||
ASSERT_EQ(strncmp(value_out, skel->data->value_bar, sizeof(skel->data->value_bar)), 0,
|
||||
"strncmp value_bar");
|
||||
}
|
||||
|
||||
static void validate_bar_removed(struct test_set_remove_xattr *skel)
|
||||
{
|
||||
char value_out[32];
|
||||
int err;
|
||||
|
||||
err = getxattr(testfile, skel->rodata->xattr_bar, value_out, sizeof(value_out));
|
||||
ASSERT_LT(err, 0, "getxattr size bar should fail");
|
||||
}
|
||||
|
||||
static void test_set_remove_xattr(void)
|
||||
{
|
||||
struct test_set_remove_xattr *skel = NULL;
|
||||
int fd = -1, err;
|
||||
|
||||
fd = open(testfile, O_CREAT | O_RDONLY, 0644);
|
||||
if (!ASSERT_GE(fd, 0, "create_file"))
|
||||
return;
|
||||
|
||||
close(fd);
|
||||
fd = -1;
|
||||
|
||||
skel = test_set_remove_xattr__open_and_load();
|
||||
if (!ASSERT_OK_PTR(skel, "test_set_remove_xattr__open_and_load"))
|
||||
return;
|
||||
|
||||
/* Set security.bpf.foo to "hello" */
|
||||
err = setxattr(testfile, skel->rodata->xattr_foo, value_foo, strlen(value_foo) + 1, 0);
|
||||
if (err && errno == EOPNOTSUPP) {
|
||||
printf("%s:SKIP:local fs doesn't support xattr (%d)\n"
|
||||
"To run this test, make sure /tmp filesystem supports xattr.\n",
|
||||
__func__, errno);
|
||||
test__skip();
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!ASSERT_OK(err, "setxattr"))
|
||||
goto out;
|
||||
|
||||
skel->bss->monitored_pid = getpid();
|
||||
err = test_set_remove_xattr__attach(skel);
|
||||
if (!ASSERT_OK(err, "test_set_remove_xattr__attach"))
|
||||
goto out;
|
||||
|
||||
/* First, test not _locked version of the kfuncs with getxattr. */
|
||||
|
||||
/* Read security.bpf.foo and trigger test_inode_getxattr. This
|
||||
* bpf program will set security.bpf.bar to "world".
|
||||
*/
|
||||
read_and_validate_foo(skel);
|
||||
validate_bar_match(skel);
|
||||
|
||||
/* Read security.bpf.foo and trigger test_inode_getxattr again.
|
||||
* This will remove xattr security.bpf.bar.
|
||||
*/
|
||||
read_and_validate_foo(skel);
|
||||
validate_bar_removed(skel);
|
||||
|
||||
ASSERT_TRUE(skel->bss->set_security_bpf_bar_success, "set_security_bpf_bar_success");
|
||||
ASSERT_TRUE(skel->bss->remove_security_bpf_bar_success, "remove_security_bpf_bar_success");
|
||||
ASSERT_TRUE(skel->bss->set_security_selinux_fail, "set_security_selinux_fail");
|
||||
ASSERT_TRUE(skel->bss->remove_security_selinux_fail, "remove_security_selinux_fail");
|
||||
|
||||
/* Second, test _locked version of the kfuncs, with setxattr */
|
||||
|
||||
/* Set security.bpf.foo and trigger test_inode_setxattr. This
|
||||
* bpf program will set security.bpf.bar to "world".
|
||||
*/
|
||||
set_foo(skel);
|
||||
validate_bar_match(skel);
|
||||
|
||||
/* Set security.bpf.foo and trigger test_inode_setxattr again.
|
||||
* This will remove xattr security.bpf.bar.
|
||||
*/
|
||||
set_foo(skel);
|
||||
validate_bar_removed(skel);
|
||||
|
||||
ASSERT_TRUE(skel->bss->locked_set_security_bpf_bar_success,
|
||||
"locked_set_security_bpf_bar_success");
|
||||
ASSERT_TRUE(skel->bss->locked_remove_security_bpf_bar_success,
|
||||
"locked_remove_security_bpf_bar_success");
|
||||
ASSERT_TRUE(skel->bss->locked_set_security_selinux_fail,
|
||||
"locked_set_security_selinux_fail");
|
||||
ASSERT_TRUE(skel->bss->locked_remove_security_selinux_fail,
|
||||
"locked_remove_security_selinux_fail");
|
||||
|
||||
out:
|
||||
close(fd);
|
||||
test_set_remove_xattr__destroy(skel);
|
||||
remove(testfile);
|
||||
}
|
||||
|
||||
#ifndef SHA256_DIGEST_SIZE
|
||||
#define SHA256_DIGEST_SIZE 32
|
||||
#endif
|
||||
@@ -141,8 +270,21 @@ out:
|
||||
|
||||
void test_fs_kfuncs(void)
|
||||
{
|
||||
if (test__start_subtest("xattr"))
|
||||
test_xattr();
|
||||
/* Matches xattr_names in progs/test_get_xattr.c */
|
||||
if (test__start_subtest("user_xattr"))
|
||||
test_get_xattr("user.kfuncs", "hello", true);
|
||||
|
||||
if (test__start_subtest("security_bpf_xattr"))
|
||||
test_get_xattr("security.bpf.xxx", "hello", true);
|
||||
|
||||
if (test__start_subtest("security_bpf_xattr_error"))
|
||||
test_get_xattr("security.bpf", "hello", false);
|
||||
|
||||
if (test__start_subtest("security_selinux_xattr_error"))
|
||||
test_get_xattr("security.selinux", "hello", false);
|
||||
|
||||
if (test__start_subtest("set_remove_xattr"))
|
||||
test_set_remove_xattr();
|
||||
|
||||
if (test__start_subtest("fsverity"))
|
||||
test_fsverity();
|
||||
|
||||
43
tools/testing/selftests/bpf/prog_tests/kernel_flag.c
Normal file
43
tools/testing/selftests/bpf/prog_tests/kernel_flag.c
Normal file
@@ -0,0 +1,43 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2025 Microsoft */
|
||||
#include <test_progs.h>
|
||||
#include "kfunc_call_test.skel.h"
|
||||
#include "kfunc_call_test.lskel.h"
|
||||
#include "test_kernel_flag.skel.h"
|
||||
|
||||
void test_kernel_flag(void)
|
||||
{
|
||||
struct test_kernel_flag *lsm_skel;
|
||||
struct kfunc_call_test *skel = NULL;
|
||||
struct kfunc_call_test_lskel *lskel = NULL;
|
||||
int ret;
|
||||
|
||||
lsm_skel = test_kernel_flag__open_and_load();
|
||||
if (!ASSERT_OK_PTR(lsm_skel, "lsm_skel"))
|
||||
return;
|
||||
|
||||
lsm_skel->bss->monitored_tid = gettid();
|
||||
|
||||
ret = test_kernel_flag__attach(lsm_skel);
|
||||
if (!ASSERT_OK(ret, "test_kernel_flag__attach"))
|
||||
goto close_prog;
|
||||
|
||||
/* Test with skel. This should pass the gatekeeper */
|
||||
skel = kfunc_call_test__open_and_load();
|
||||
if (!ASSERT_OK_PTR(skel, "skel"))
|
||||
goto close_prog;
|
||||
|
||||
/* Test with lskel. This should fail due to blocking kernel-based bpf() invocations */
|
||||
lskel = kfunc_call_test_lskel__open_and_load();
|
||||
if (!ASSERT_ERR_PTR(lskel, "lskel"))
|
||||
goto close_prog;
|
||||
|
||||
close_prog:
|
||||
if (skel)
|
||||
kfunc_call_test__destroy(skel);
|
||||
if (lskel)
|
||||
kfunc_call_test_lskel__destroy(lskel);
|
||||
|
||||
lsm_skel->bss->monitored_tid = 0;
|
||||
test_kernel_flag__destroy(lsm_skel);
|
||||
}
|
||||
540
tools/testing/selftests/bpf/prog_tests/lwt_ip_encap.c
Normal file
540
tools/testing/selftests/bpf/prog_tests/lwt_ip_encap.c
Normal file
@@ -0,0 +1,540 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include "network_helpers.h"
|
||||
#include "test_progs.h"
|
||||
|
||||
#define BPF_FILE "test_lwt_ip_encap.bpf.o"
|
||||
|
||||
#define NETNS_NAME_SIZE 32
|
||||
#define NETNS_BASE "ns-lwt-ip-encap"
|
||||
|
||||
#define IP4_ADDR_1 "172.16.1.100"
|
||||
#define IP4_ADDR_2 "172.16.2.100"
|
||||
#define IP4_ADDR_3 "172.16.3.100"
|
||||
#define IP4_ADDR_4 "172.16.4.100"
|
||||
#define IP4_ADDR_5 "172.16.5.100"
|
||||
#define IP4_ADDR_6 "172.16.6.100"
|
||||
#define IP4_ADDR_7 "172.16.7.100"
|
||||
#define IP4_ADDR_8 "172.16.8.100"
|
||||
#define IP4_ADDR_GRE "172.16.16.100"
|
||||
|
||||
#define IP4_ADDR_SRC IP4_ADDR_1
|
||||
#define IP4_ADDR_DST IP4_ADDR_4
|
||||
|
||||
#define IP6_ADDR_1 "fb01::1"
|
||||
#define IP6_ADDR_2 "fb02::1"
|
||||
#define IP6_ADDR_3 "fb03::1"
|
||||
#define IP6_ADDR_4 "fb04::1"
|
||||
#define IP6_ADDR_5 "fb05::1"
|
||||
#define IP6_ADDR_6 "fb06::1"
|
||||
#define IP6_ADDR_7 "fb07::1"
|
||||
#define IP6_ADDR_8 "fb08::1"
|
||||
#define IP6_ADDR_GRE "fb10::1"
|
||||
|
||||
#define IP6_ADDR_SRC IP6_ADDR_1
|
||||
#define IP6_ADDR_DST IP6_ADDR_4
|
||||
|
||||
/* Setup/topology:
|
||||
*
|
||||
* NS1 NS2 NS3
|
||||
* veth1 <---> veth2 veth3 <---> veth4 (the top route)
|
||||
* veth5 <---> veth6 veth7 <---> veth8 (the bottom route)
|
||||
*
|
||||
* Each vethN gets IP[4|6]_ADDR_N address.
|
||||
*
|
||||
* IP*_ADDR_SRC = IP*_ADDR_1
|
||||
* IP*_ADDR_DST = IP*_ADDR_4
|
||||
*
|
||||
* All tests test pings from IP*_ADDR__SRC to IP*_ADDR_DST.
|
||||
*
|
||||
* By default, routes are configured to allow packets to go
|
||||
* IP*_ADDR_1 <=> IP*_ADDR_2 <=> IP*_ADDR_3 <=> IP*_ADDR_4 (the top route).
|
||||
*
|
||||
* A GRE device is installed in NS3 with IP*_ADDR_GRE, and
|
||||
* NS1/NS2 are configured to route packets to IP*_ADDR_GRE via IP*_ADDR_8
|
||||
* (the bottom route).
|
||||
*
|
||||
* Tests:
|
||||
*
|
||||
* 1. Routes NS2->IP*_ADDR_DST are brought down, so the only way a ping
|
||||
* from IP*_ADDR_SRC to IP*_ADDR_DST can work is via IP*_ADDR_GRE.
|
||||
*
|
||||
* 2a. In an egress test, a bpf LWT_XMIT program is installed on veth1
|
||||
* that encaps the packets with an IP/GRE header to route to IP*_ADDR_GRE.
|
||||
*
|
||||
* ping: SRC->[encap at veth1:egress]->GRE:decap->DST
|
||||
* ping replies go DST->SRC directly
|
||||
*
|
||||
* 2b. In an ingress test, a bpf LWT_IN program is installed on veth2
|
||||
* that encaps the packets with an IP/GRE header to route to IP*_ADDR_GRE.
|
||||
*
|
||||
* ping: SRC->[encap at veth2:ingress]->GRE:decap->DST
|
||||
* ping replies go DST->SRC directly
|
||||
*/
|
||||
|
||||
static int create_ns(char *name, size_t name_sz)
|
||||
{
|
||||
if (!name)
|
||||
goto fail;
|
||||
|
||||
if (!ASSERT_OK(append_tid(name, name_sz), "append TID"))
|
||||
goto fail;
|
||||
|
||||
SYS(fail, "ip netns add %s", name);
|
||||
|
||||
/* rp_filter gets confused by what these tests are doing, so disable it */
|
||||
SYS(fail, "ip netns exec %s sysctl -wq net.ipv4.conf.all.rp_filter=0", name);
|
||||
SYS(fail, "ip netns exec %s sysctl -wq net.ipv4.conf.default.rp_filter=0", name);
|
||||
/* Disable IPv6 DAD because it sometimes takes too long and fails tests */
|
||||
SYS(fail, "ip netns exec %s sysctl -wq net.ipv6.conf.all.accept_dad=0", name);
|
||||
SYS(fail, "ip netns exec %s sysctl -wq net.ipv6.conf.default.accept_dad=0", name);
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int set_top_addr(const char *ns1, const char *ns2, const char *ns3)
|
||||
{
|
||||
SYS(fail, "ip -n %s a add %s/24 dev veth1", ns1, IP4_ADDR_1);
|
||||
SYS(fail, "ip -n %s a add %s/24 dev veth2", ns2, IP4_ADDR_2);
|
||||
SYS(fail, "ip -n %s a add %s/24 dev veth3", ns2, IP4_ADDR_3);
|
||||
SYS(fail, "ip -n %s a add %s/24 dev veth4", ns3, IP4_ADDR_4);
|
||||
SYS(fail, "ip -n %s -6 a add %s/128 dev veth1", ns1, IP6_ADDR_1);
|
||||
SYS(fail, "ip -n %s -6 a add %s/128 dev veth2", ns2, IP6_ADDR_2);
|
||||
SYS(fail, "ip -n %s -6 a add %s/128 dev veth3", ns2, IP6_ADDR_3);
|
||||
SYS(fail, "ip -n %s -6 a add %s/128 dev veth4", ns3, IP6_ADDR_4);
|
||||
|
||||
SYS(fail, "ip -n %s link set dev veth1 up", ns1);
|
||||
SYS(fail, "ip -n %s link set dev veth2 up", ns2);
|
||||
SYS(fail, "ip -n %s link set dev veth3 up", ns2);
|
||||
SYS(fail, "ip -n %s link set dev veth4 up", ns3);
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int set_bottom_addr(const char *ns1, const char *ns2, const char *ns3)
|
||||
{
|
||||
SYS(fail, "ip -n %s a add %s/24 dev veth5", ns1, IP4_ADDR_5);
|
||||
SYS(fail, "ip -n %s a add %s/24 dev veth6", ns2, IP4_ADDR_6);
|
||||
SYS(fail, "ip -n %s a add %s/24 dev veth7", ns2, IP4_ADDR_7);
|
||||
SYS(fail, "ip -n %s a add %s/24 dev veth8", ns3, IP4_ADDR_8);
|
||||
SYS(fail, "ip -n %s -6 a add %s/128 dev veth5", ns1, IP6_ADDR_5);
|
||||
SYS(fail, "ip -n %s -6 a add %s/128 dev veth6", ns2, IP6_ADDR_6);
|
||||
SYS(fail, "ip -n %s -6 a add %s/128 dev veth7", ns2, IP6_ADDR_7);
|
||||
SYS(fail, "ip -n %s -6 a add %s/128 dev veth8", ns3, IP6_ADDR_8);
|
||||
|
||||
SYS(fail, "ip -n %s link set dev veth5 up", ns1);
|
||||
SYS(fail, "ip -n %s link set dev veth6 up", ns2);
|
||||
SYS(fail, "ip -n %s link set dev veth7 up", ns2);
|
||||
SYS(fail, "ip -n %s link set dev veth8 up", ns3);
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int configure_vrf(const char *ns1, const char *ns2)
|
||||
{
|
||||
if (!ns1 || !ns2)
|
||||
goto fail;
|
||||
|
||||
SYS(fail, "ip -n %s link add red type vrf table 1001", ns1);
|
||||
SYS(fail, "ip -n %s link set red up", ns1);
|
||||
SYS(fail, "ip -n %s route add table 1001 unreachable default metric 8192", ns1);
|
||||
SYS(fail, "ip -n %s -6 route add table 1001 unreachable default metric 8192", ns1);
|
||||
SYS(fail, "ip -n %s link set veth1 vrf red", ns1);
|
||||
SYS(fail, "ip -n %s link set veth5 vrf red", ns1);
|
||||
|
||||
SYS(fail, "ip -n %s link add red type vrf table 1001", ns2);
|
||||
SYS(fail, "ip -n %s link set red up", ns2);
|
||||
SYS(fail, "ip -n %s route add table 1001 unreachable default metric 8192", ns2);
|
||||
SYS(fail, "ip -n %s -6 route add table 1001 unreachable default metric 8192", ns2);
|
||||
SYS(fail, "ip -n %s link set veth2 vrf red", ns2);
|
||||
SYS(fail, "ip -n %s link set veth3 vrf red", ns2);
|
||||
SYS(fail, "ip -n %s link set veth6 vrf red", ns2);
|
||||
SYS(fail, "ip -n %s link set veth7 vrf red", ns2);
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int configure_ns1(const char *ns1, const char *vrf)
|
||||
{
|
||||
struct nstoken *nstoken = NULL;
|
||||
|
||||
if (!ns1 || !vrf)
|
||||
goto fail;
|
||||
|
||||
nstoken = open_netns(ns1);
|
||||
if (!ASSERT_OK_PTR(nstoken, "open ns1"))
|
||||
goto fail;
|
||||
|
||||
/* Top route */
|
||||
SYS(fail, "ip route add %s/32 dev veth1 %s", IP4_ADDR_2, vrf);
|
||||
SYS(fail, "ip route add default dev veth1 via %s %s", IP4_ADDR_2, vrf);
|
||||
SYS(fail, "ip -6 route add %s/128 dev veth1 %s", IP6_ADDR_2, vrf);
|
||||
SYS(fail, "ip -6 route add default dev veth1 via %s %s", IP6_ADDR_2, vrf);
|
||||
/* Bottom route */
|
||||
SYS(fail, "ip route add %s/32 dev veth5 %s", IP4_ADDR_6, vrf);
|
||||
SYS(fail, "ip route add %s/32 dev veth5 via %s %s", IP4_ADDR_7, IP4_ADDR_6, vrf);
|
||||
SYS(fail, "ip route add %s/32 dev veth5 via %s %s", IP4_ADDR_8, IP4_ADDR_6, vrf);
|
||||
SYS(fail, "ip -6 route add %s/128 dev veth5 %s", IP6_ADDR_6, vrf);
|
||||
SYS(fail, "ip -6 route add %s/128 dev veth5 via %s %s", IP6_ADDR_7, IP6_ADDR_6, vrf);
|
||||
SYS(fail, "ip -6 route add %s/128 dev veth5 via %s %s", IP6_ADDR_8, IP6_ADDR_6, vrf);
|
||||
|
||||
close_netns(nstoken);
|
||||
return 0;
|
||||
fail:
|
||||
close_netns(nstoken);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int configure_ns2(const char *ns2, const char *vrf)
|
||||
{
|
||||
struct nstoken *nstoken = NULL;
|
||||
|
||||
if (!ns2 || !vrf)
|
||||
goto fail;
|
||||
|
||||
nstoken = open_netns(ns2);
|
||||
if (!ASSERT_OK_PTR(nstoken, "open ns2"))
|
||||
goto fail;
|
||||
|
||||
SYS(fail, "ip netns exec %s sysctl -wq net.ipv4.ip_forward=1", ns2);
|
||||
SYS(fail, "ip netns exec %s sysctl -wq net.ipv6.conf.all.forwarding=1", ns2);
|
||||
|
||||
/* Top route */
|
||||
SYS(fail, "ip route add %s/32 dev veth2 %s", IP4_ADDR_1, vrf);
|
||||
SYS(fail, "ip route add %s/32 dev veth3 %s", IP4_ADDR_4, vrf);
|
||||
SYS(fail, "ip -6 route add %s/128 dev veth2 %s", IP6_ADDR_1, vrf);
|
||||
SYS(fail, "ip -6 route add %s/128 dev veth3 %s", IP6_ADDR_4, vrf);
|
||||
/* Bottom route */
|
||||
SYS(fail, "ip route add %s/32 dev veth6 %s", IP4_ADDR_5, vrf);
|
||||
SYS(fail, "ip route add %s/32 dev veth7 %s", IP4_ADDR_8, vrf);
|
||||
SYS(fail, "ip -6 route add %s/128 dev veth6 %s", IP6_ADDR_5, vrf);
|
||||
SYS(fail, "ip -6 route add %s/128 dev veth7 %s", IP6_ADDR_8, vrf);
|
||||
|
||||
close_netns(nstoken);
|
||||
return 0;
|
||||
fail:
|
||||
close_netns(nstoken);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int configure_ns3(const char *ns3)
|
||||
{
|
||||
struct nstoken *nstoken = NULL;
|
||||
|
||||
if (!ns3)
|
||||
goto fail;
|
||||
|
||||
nstoken = open_netns(ns3);
|
||||
if (!ASSERT_OK_PTR(nstoken, "open ns3"))
|
||||
goto fail;
|
||||
|
||||
/* Top route */
|
||||
SYS(fail, "ip route add %s/32 dev veth4", IP4_ADDR_3);
|
||||
SYS(fail, "ip route add %s/32 dev veth4 via %s", IP4_ADDR_1, IP4_ADDR_3);
|
||||
SYS(fail, "ip route add %s/32 dev veth4 via %s", IP4_ADDR_2, IP4_ADDR_3);
|
||||
SYS(fail, "ip -6 route add %s/128 dev veth4", IP6_ADDR_3);
|
||||
SYS(fail, "ip -6 route add %s/128 dev veth4 via %s", IP6_ADDR_1, IP6_ADDR_3);
|
||||
SYS(fail, "ip -6 route add %s/128 dev veth4 via %s", IP6_ADDR_2, IP6_ADDR_3);
|
||||
/* Bottom route */
|
||||
SYS(fail, "ip route add %s/32 dev veth8", IP4_ADDR_7);
|
||||
SYS(fail, "ip route add %s/32 dev veth8 via %s", IP4_ADDR_5, IP4_ADDR_7);
|
||||
SYS(fail, "ip route add %s/32 dev veth8 via %s", IP4_ADDR_6, IP4_ADDR_7);
|
||||
SYS(fail, "ip -6 route add %s/128 dev veth8", IP6_ADDR_7);
|
||||
SYS(fail, "ip -6 route add %s/128 dev veth8 via %s", IP6_ADDR_5, IP6_ADDR_7);
|
||||
SYS(fail, "ip -6 route add %s/128 dev veth8 via %s", IP6_ADDR_6, IP6_ADDR_7);
|
||||
|
||||
/* Configure IPv4 GRE device */
|
||||
SYS(fail, "ip tunnel add gre_dev mode gre remote %s local %s ttl 255",
|
||||
IP4_ADDR_1, IP4_ADDR_GRE);
|
||||
SYS(fail, "ip link set gre_dev up");
|
||||
SYS(fail, "ip a add %s dev gre_dev", IP4_ADDR_GRE);
|
||||
|
||||
/* Configure IPv6 GRE device */
|
||||
SYS(fail, "ip tunnel add gre6_dev mode ip6gre remote %s local %s ttl 255",
|
||||
IP6_ADDR_1, IP6_ADDR_GRE);
|
||||
SYS(fail, "ip link set gre6_dev up");
|
||||
SYS(fail, "ip a add %s dev gre6_dev", IP6_ADDR_GRE);
|
||||
|
||||
close_netns(nstoken);
|
||||
return 0;
|
||||
fail:
|
||||
close_netns(nstoken);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int setup_network(char *ns1, char *ns2, char *ns3, const char *vrf)
|
||||
{
|
||||
if (!ns1 || !ns2 || !ns3 || !vrf)
|
||||
goto fail;
|
||||
|
||||
SYS(fail, "ip -n %s link add veth1 type veth peer name veth2 netns %s", ns1, ns2);
|
||||
SYS(fail, "ip -n %s link add veth3 type veth peer name veth4 netns %s", ns2, ns3);
|
||||
SYS(fail, "ip -n %s link add veth5 type veth peer name veth6 netns %s", ns1, ns2);
|
||||
SYS(fail, "ip -n %s link add veth7 type veth peer name veth8 netns %s", ns2, ns3);
|
||||
|
||||
if (vrf[0]) {
|
||||
if (!ASSERT_OK(configure_vrf(ns1, ns2), "configure vrf"))
|
||||
goto fail;
|
||||
}
|
||||
if (!ASSERT_OK(set_top_addr(ns1, ns2, ns3), "set top addresses"))
|
||||
goto fail;
|
||||
|
||||
if (!ASSERT_OK(set_bottom_addr(ns1, ns2, ns3), "set bottom addresses"))
|
||||
goto fail;
|
||||
|
||||
if (!ASSERT_OK(configure_ns1(ns1, vrf), "configure ns1 routes"))
|
||||
goto fail;
|
||||
|
||||
if (!ASSERT_OK(configure_ns2(ns2, vrf), "configure ns2 routes"))
|
||||
goto fail;
|
||||
|
||||
if (!ASSERT_OK(configure_ns3(ns3), "configure ns3 routes"))
|
||||
goto fail;
|
||||
|
||||
/* Link bottom route to the GRE tunnels */
|
||||
SYS(fail, "ip -n %s route add %s/32 dev veth5 via %s %s",
|
||||
ns1, IP4_ADDR_GRE, IP4_ADDR_6, vrf);
|
||||
SYS(fail, "ip -n %s route add %s/32 dev veth7 via %s %s",
|
||||
ns2, IP4_ADDR_GRE, IP4_ADDR_8, vrf);
|
||||
SYS(fail, "ip -n %s -6 route add %s/128 dev veth5 via %s %s",
|
||||
ns1, IP6_ADDR_GRE, IP6_ADDR_6, vrf);
|
||||
SYS(fail, "ip -n %s -6 route add %s/128 dev veth7 via %s %s",
|
||||
ns2, IP6_ADDR_GRE, IP6_ADDR_8, vrf);
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int remove_routes_to_gredev(const char *ns1, const char *ns2, const char *vrf)
|
||||
{
|
||||
SYS(fail, "ip -n %s route del %s dev veth5 %s", ns1, IP4_ADDR_GRE, vrf);
|
||||
SYS(fail, "ip -n %s route del %s dev veth7 %s", ns2, IP4_ADDR_GRE, vrf);
|
||||
SYS(fail, "ip -n %s -6 route del %s/128 dev veth5 %s", ns1, IP6_ADDR_GRE, vrf);
|
||||
SYS(fail, "ip -n %s -6 route del %s/128 dev veth7 %s", ns2, IP6_ADDR_GRE, vrf);
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int add_unreachable_routes_to_gredev(const char *ns1, const char *ns2, const char *vrf)
|
||||
{
|
||||
SYS(fail, "ip -n %s route add unreachable %s/32 %s", ns1, IP4_ADDR_GRE, vrf);
|
||||
SYS(fail, "ip -n %s route add unreachable %s/32 %s", ns2, IP4_ADDR_GRE, vrf);
|
||||
SYS(fail, "ip -n %s -6 route add unreachable %s/128 %s", ns1, IP6_ADDR_GRE, vrf);
|
||||
SYS(fail, "ip -n %s -6 route add unreachable %s/128 %s", ns2, IP6_ADDR_GRE, vrf);
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
#define GSO_SIZE 5000
|
||||
#define GSO_TCP_PORT 9000
|
||||
/* This tests the fix from commit ea0371f78799 ("net: fix GSO in bpf_lwt_push_ip_encap") */
|
||||
static int test_gso_fix(const char *ns1, const char *ns3, int family)
|
||||
{
|
||||
const char *ip_addr = family == AF_INET ? IP4_ADDR_DST : IP6_ADDR_DST;
|
||||
char gso_packet[GSO_SIZE] = {};
|
||||
struct nstoken *nstoken = NULL;
|
||||
int sfd, cfd, afd;
|
||||
ssize_t bytes;
|
||||
int ret = -1;
|
||||
|
||||
if (!ns1 || !ns3)
|
||||
return ret;
|
||||
|
||||
nstoken = open_netns(ns3);
|
||||
if (!ASSERT_OK_PTR(nstoken, "open ns3"))
|
||||
return ret;
|
||||
|
||||
sfd = start_server_str(family, SOCK_STREAM, ip_addr, GSO_TCP_PORT, NULL);
|
||||
if (!ASSERT_OK_FD(sfd, "start server"))
|
||||
goto close_netns;
|
||||
|
||||
close_netns(nstoken);
|
||||
|
||||
nstoken = open_netns(ns1);
|
||||
if (!ASSERT_OK_PTR(nstoken, "open ns1"))
|
||||
goto close_server;
|
||||
|
||||
cfd = connect_to_addr_str(family, SOCK_STREAM, ip_addr, GSO_TCP_PORT, NULL);
|
||||
if (!ASSERT_OK_FD(cfd, "connect to server"))
|
||||
goto close_server;
|
||||
|
||||
close_netns(nstoken);
|
||||
nstoken = NULL;
|
||||
|
||||
afd = accept(sfd, NULL, NULL);
|
||||
if (!ASSERT_OK_FD(afd, "accept"))
|
||||
goto close_client;
|
||||
|
||||
/* Send a packet larger than MTU */
|
||||
bytes = send(cfd, gso_packet, GSO_SIZE, 0);
|
||||
if (!ASSERT_EQ(bytes, GSO_SIZE, "send packet"))
|
||||
goto close_accept;
|
||||
|
||||
/* Verify we received all expected bytes */
|
||||
bytes = read(afd, gso_packet, GSO_SIZE);
|
||||
if (!ASSERT_EQ(bytes, GSO_SIZE, "receive packet"))
|
||||
goto close_accept;
|
||||
|
||||
ret = 0;
|
||||
|
||||
close_accept:
|
||||
close(afd);
|
||||
close_client:
|
||||
close(cfd);
|
||||
close_server:
|
||||
close(sfd);
|
||||
close_netns:
|
||||
close_netns(nstoken);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int check_ping_ok(const char *ns1)
|
||||
{
|
||||
SYS(fail, "ip netns exec %s ping -c 1 -W1 -I veth1 %s > /dev/null", ns1, IP4_ADDR_DST);
|
||||
SYS(fail, "ip netns exec %s ping6 -c 1 -W1 -I veth1 %s > /dev/null", ns1, IP6_ADDR_DST);
|
||||
return 0;
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int check_ping_fails(const char *ns1)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = SYS_NOFAIL("ip netns exec %s ping -c 1 -W1 -I veth1 %s", ns1, IP4_ADDR_DST);
|
||||
if (!ret)
|
||||
return -1;
|
||||
|
||||
ret = SYS_NOFAIL("ip netns exec %s ping6 -c 1 -W1 -I veth1 %s", ns1, IP6_ADDR_DST);
|
||||
if (!ret)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define EGRESS true
|
||||
#define INGRESS false
|
||||
#define IPV4_ENCAP true
|
||||
#define IPV6_ENCAP false
|
||||
static void lwt_ip_encap(bool ipv4_encap, bool egress, const char *vrf)
|
||||
{
|
||||
char ns1[NETNS_NAME_SIZE] = NETNS_BASE "-1-";
|
||||
char ns2[NETNS_NAME_SIZE] = NETNS_BASE "-2-";
|
||||
char ns3[NETNS_NAME_SIZE] = NETNS_BASE "-3-";
|
||||
char *sec = ipv4_encap ? "encap_gre" : "encap_gre6";
|
||||
|
||||
if (!vrf)
|
||||
return;
|
||||
|
||||
if (!ASSERT_OK(create_ns(ns1, NETNS_NAME_SIZE), "create ns1"))
|
||||
goto out;
|
||||
if (!ASSERT_OK(create_ns(ns2, NETNS_NAME_SIZE), "create ns2"))
|
||||
goto out;
|
||||
if (!ASSERT_OK(create_ns(ns3, NETNS_NAME_SIZE), "create ns3"))
|
||||
goto out;
|
||||
|
||||
if (!ASSERT_OK(setup_network(ns1, ns2, ns3, vrf), "setup network"))
|
||||
goto out;
|
||||
|
||||
/* By default, pings work */
|
||||
if (!ASSERT_OK(check_ping_ok(ns1), "ping OK"))
|
||||
goto out;
|
||||
|
||||
/* Remove NS2->DST routes, ping fails */
|
||||
SYS(out, "ip -n %s route del %s/32 dev veth3 %s", ns2, IP4_ADDR_DST, vrf);
|
||||
SYS(out, "ip -n %s -6 route del %s/128 dev veth3 %s", ns2, IP6_ADDR_DST, vrf);
|
||||
if (!ASSERT_OK(check_ping_fails(ns1), "ping expected fail"))
|
||||
goto out;
|
||||
|
||||
/* Install replacement routes (LWT/eBPF), pings succeed */
|
||||
if (egress) {
|
||||
SYS(out, "ip -n %s route add %s encap bpf xmit obj %s sec %s dev veth1 %s",
|
||||
ns1, IP4_ADDR_DST, BPF_FILE, sec, vrf);
|
||||
SYS(out, "ip -n %s -6 route add %s encap bpf xmit obj %s sec %s dev veth1 %s",
|
||||
ns1, IP6_ADDR_DST, BPF_FILE, sec, vrf);
|
||||
} else {
|
||||
SYS(out, "ip -n %s route add %s encap bpf in obj %s sec %s dev veth2 %s",
|
||||
ns2, IP4_ADDR_DST, BPF_FILE, sec, vrf);
|
||||
SYS(out, "ip -n %s -6 route add %s encap bpf in obj %s sec %s dev veth2 %s",
|
||||
ns2, IP6_ADDR_DST, BPF_FILE, sec, vrf);
|
||||
}
|
||||
|
||||
if (!ASSERT_OK(check_ping_ok(ns1), "ping OK"))
|
||||
goto out;
|
||||
|
||||
/* Skip GSO tests with VRF: VRF routing needs properly assigned
|
||||
* source IP/device, which is easy to do with ping but hard with TCP.
|
||||
*/
|
||||
if (egress && !vrf[0]) {
|
||||
if (!ASSERT_OK(test_gso_fix(ns1, ns3, AF_INET), "test GSO"))
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Negative test: remove routes to GRE devices: ping fails */
|
||||
if (!ASSERT_OK(remove_routes_to_gredev(ns1, ns2, vrf), "remove routes to gredev"))
|
||||
goto out;
|
||||
if (!ASSERT_OK(check_ping_fails(ns1), "ping expected fail"))
|
||||
goto out;
|
||||
|
||||
/* Another negative test */
|
||||
if (!ASSERT_OK(add_unreachable_routes_to_gredev(ns1, ns2, vrf),
|
||||
"add unreachable routes"))
|
||||
goto out;
|
||||
ASSERT_OK(check_ping_fails(ns1), "ping expected fail");
|
||||
|
||||
out:
|
||||
SYS_NOFAIL("ip netns del %s", ns1);
|
||||
SYS_NOFAIL("ip netns del %s", ns2);
|
||||
SYS_NOFAIL("ip netns del %s", ns3);
|
||||
}
|
||||
|
||||
void test_lwt_ip_encap_vrf_ipv6(void)
|
||||
{
|
||||
if (test__start_subtest("egress"))
|
||||
lwt_ip_encap(IPV6_ENCAP, EGRESS, "vrf red");
|
||||
|
||||
if (test__start_subtest("ingress"))
|
||||
lwt_ip_encap(IPV6_ENCAP, INGRESS, "vrf red");
|
||||
}
|
||||
|
||||
void test_lwt_ip_encap_vrf_ipv4(void)
|
||||
{
|
||||
if (test__start_subtest("egress"))
|
||||
lwt_ip_encap(IPV4_ENCAP, EGRESS, "vrf red");
|
||||
|
||||
if (test__start_subtest("ingress"))
|
||||
lwt_ip_encap(IPV4_ENCAP, INGRESS, "vrf red");
|
||||
}
|
||||
|
||||
void test_lwt_ip_encap_ipv6(void)
|
||||
{
|
||||
if (test__start_subtest("egress"))
|
||||
lwt_ip_encap(IPV6_ENCAP, EGRESS, "");
|
||||
|
||||
if (test__start_subtest("ingress"))
|
||||
lwt_ip_encap(IPV6_ENCAP, INGRESS, "");
|
||||
}
|
||||
|
||||
void test_lwt_ip_encap_ipv4(void)
|
||||
{
|
||||
if (test__start_subtest("egress"))
|
||||
lwt_ip_encap(IPV4_ENCAP, EGRESS, "");
|
||||
|
||||
if (test__start_subtest("ingress"))
|
||||
lwt_ip_encap(IPV4_ENCAP, INGRESS, "");
|
||||
}
|
||||
176
tools/testing/selftests/bpf/prog_tests/lwt_seg6local.c
Normal file
176
tools/testing/selftests/bpf/prog_tests/lwt_seg6local.c
Normal file
@@ -0,0 +1,176 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
/* Connects 6 network namespaces through veths.
|
||||
* Each NS may have different IPv6 global scope addresses :
|
||||
*
|
||||
* NS1 NS2 NS3 NS4 NS5 NS6
|
||||
* lo veth1 <-> veth2 veth3 <-> veth4 veth5 <-> veth6 lo veth7 <-> veth8 veth9 <-> veth10 lo
|
||||
* fb00 ::1 ::12 ::21 ::34 ::43 ::56 ::65 ::78 ::87 ::910 ::109 ::6
|
||||
* fd00 ::4
|
||||
* fc42 ::1
|
||||
*
|
||||
* All IPv6 packets going to fb00::/16 through NS2 will be encapsulated in a
|
||||
* IPv6 header with a Segment Routing Header, with segments :
|
||||
* fd00::1 -> fd00::2 -> fd00::3 -> fd00::4
|
||||
*
|
||||
* 3 fd00::/16 IPv6 addresses are binded to seg6local End.BPF actions :
|
||||
* - fd00::1 : add a TLV, change the flags and apply a End.X action to fc42::1
|
||||
* - fd00::2 : remove the TLV, change the flags, add a tag
|
||||
* - fd00::3 : apply an End.T action to fd00::4, through routing table 117
|
||||
*
|
||||
* fd00::4 is a simple Segment Routing node decapsulating the inner IPv6 packet.
|
||||
* Each End.BPF action will validate the operations applied on the SRH by the
|
||||
* previous BPF program in the chain, otherwise the packet is dropped.
|
||||
*
|
||||
* An UDP datagram is sent from fb00::1 to fb00::6. The test succeeds if this
|
||||
* datagram can be read on NS6 when binding to fb00::6.
|
||||
*/
|
||||
|
||||
#include "network_helpers.h"
|
||||
#include "test_progs.h"
|
||||
|
||||
#define NETNS_BASE "lwt-seg6local-"
|
||||
#define BPF_FILE "test_lwt_seg6local.bpf.o"
|
||||
|
||||
static void cleanup(void)
|
||||
{
|
||||
int ns;
|
||||
|
||||
for (ns = 1; ns < 7; ns++)
|
||||
SYS_NOFAIL("ip netns del %s%d", NETNS_BASE, ns);
|
||||
}
|
||||
|
||||
static int setup(void)
|
||||
{
|
||||
int ns;
|
||||
|
||||
for (ns = 1; ns < 7; ns++)
|
||||
SYS(fail, "ip netns add %s%d", NETNS_BASE, ns);
|
||||
|
||||
SYS(fail, "ip -n %s6 link set dev lo up", NETNS_BASE);
|
||||
|
||||
for (ns = 1; ns < 6; ns++) {
|
||||
int local_id = ns * 2 - 1;
|
||||
int peer_id = ns * 2;
|
||||
int next_ns = ns + 1;
|
||||
|
||||
SYS(fail, "ip -n %s%d link add veth%d type veth peer name veth%d netns %s%d",
|
||||
NETNS_BASE, ns, local_id, peer_id, NETNS_BASE, next_ns);
|
||||
|
||||
SYS(fail, "ip -n %s%d link set dev veth%d up", NETNS_BASE, ns, local_id);
|
||||
SYS(fail, "ip -n %s%d link set dev veth%d up", NETNS_BASE, next_ns, peer_id);
|
||||
|
||||
/* All link scope addresses to veths */
|
||||
SYS(fail, "ip -n %s%d -6 addr add fb00::%d%d/16 dev veth%d scope link",
|
||||
NETNS_BASE, ns, local_id, peer_id, local_id);
|
||||
SYS(fail, "ip -n %s%d -6 addr add fb00::%d%d/16 dev veth%d scope link",
|
||||
NETNS_BASE, next_ns, peer_id, local_id, peer_id);
|
||||
}
|
||||
|
||||
|
||||
SYS(fail, "ip -n %s5 -6 route add fb00::109 table 117 dev veth9 scope link", NETNS_BASE);
|
||||
|
||||
SYS(fail, "ip -n %s1 -6 addr add fb00::1/16 dev lo", NETNS_BASE);
|
||||
SYS(fail, "ip -n %s1 -6 route add fb00::6 dev veth1 via fb00::21", NETNS_BASE);
|
||||
|
||||
SYS(fail, "ip -n %s2 -6 route add fb00::6 encap bpf in obj %s sec encap_srh dev veth2",
|
||||
NETNS_BASE, BPF_FILE);
|
||||
SYS(fail, "ip -n %s2 -6 route add fd00::1 dev veth3 via fb00::43 scope link", NETNS_BASE);
|
||||
|
||||
SYS(fail, "ip -n %s3 -6 route add fc42::1 dev veth5 via fb00::65", NETNS_BASE);
|
||||
SYS(fail,
|
||||
"ip -n %s3 -6 route add fd00::1 encap seg6local action End.BPF endpoint obj %s sec add_egr_x dev veth4",
|
||||
NETNS_BASE, BPF_FILE);
|
||||
|
||||
SYS(fail,
|
||||
"ip -n %s4 -6 route add fd00::2 encap seg6local action End.BPF endpoint obj %s sec pop_egr dev veth6",
|
||||
NETNS_BASE, BPF_FILE);
|
||||
SYS(fail, "ip -n %s4 -6 addr add fc42::1 dev lo", NETNS_BASE);
|
||||
SYS(fail, "ip -n %s4 -6 route add fd00::3 dev veth7 via fb00::87", NETNS_BASE);
|
||||
|
||||
SYS(fail, "ip -n %s5 -6 route add fd00::4 table 117 dev veth9 via fb00::109", NETNS_BASE);
|
||||
SYS(fail,
|
||||
"ip -n %s5 -6 route add fd00::3 encap seg6local action End.BPF endpoint obj %s sec inspect_t dev veth8",
|
||||
NETNS_BASE, BPF_FILE);
|
||||
|
||||
SYS(fail, "ip -n %s6 -6 addr add fb00::6/16 dev lo", NETNS_BASE);
|
||||
SYS(fail, "ip -n %s6 -6 addr add fd00::4/16 dev lo", NETNS_BASE);
|
||||
|
||||
for (ns = 1; ns < 6; ns++)
|
||||
SYS(fail, "ip netns exec %s%d sysctl -wq net.ipv6.conf.all.forwarding=1",
|
||||
NETNS_BASE, ns);
|
||||
|
||||
SYS(fail, "ip netns exec %s6 sysctl -wq net.ipv6.conf.all.seg6_enabled=1", NETNS_BASE);
|
||||
SYS(fail, "ip netns exec %s6 sysctl -wq net.ipv6.conf.lo.seg6_enabled=1", NETNS_BASE);
|
||||
SYS(fail, "ip netns exec %s6 sysctl -wq net.ipv6.conf.veth10.seg6_enabled=1", NETNS_BASE);
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
#define SERVER_PORT 7330
|
||||
#define CLIENT_PORT 2121
|
||||
void test_lwt_seg6local(void)
|
||||
{
|
||||
struct sockaddr_in6 server_addr = {};
|
||||
const char *ns1 = NETNS_BASE "1";
|
||||
const char *ns6 = NETNS_BASE "6";
|
||||
struct nstoken *nstoken = NULL;
|
||||
const char *foobar = "foobar";
|
||||
ssize_t bytes;
|
||||
int sfd, cfd;
|
||||
char buf[7];
|
||||
|
||||
if (!ASSERT_OK(setup(), "setup"))
|
||||
goto out;
|
||||
|
||||
nstoken = open_netns(ns6);
|
||||
if (!ASSERT_OK_PTR(nstoken, "open ns6"))
|
||||
goto out;
|
||||
|
||||
sfd = start_server_str(AF_INET6, SOCK_DGRAM, "fb00::6", SERVER_PORT, NULL);
|
||||
if (!ASSERT_OK_FD(sfd, "start server"))
|
||||
goto close_netns;
|
||||
|
||||
close_netns(nstoken);
|
||||
|
||||
nstoken = open_netns(ns1);
|
||||
if (!ASSERT_OK_PTR(nstoken, "open ns1"))
|
||||
goto close_server;
|
||||
|
||||
cfd = start_server_str(AF_INET6, SOCK_DGRAM, "fb00::1", CLIENT_PORT, NULL);
|
||||
if (!ASSERT_OK_FD(cfd, "start client"))
|
||||
goto close_server;
|
||||
|
||||
close_netns(nstoken);
|
||||
nstoken = NULL;
|
||||
|
||||
/* Send a packet larger than MTU */
|
||||
server_addr.sin6_family = AF_INET6;
|
||||
server_addr.sin6_port = htons(SERVER_PORT);
|
||||
if (!ASSERT_EQ(inet_pton(AF_INET6, "fb00::6", &server_addr.sin6_addr), 1,
|
||||
"build target addr"))
|
||||
goto close_client;
|
||||
|
||||
bytes = sendto(cfd, foobar, sizeof(foobar), 0,
|
||||
(struct sockaddr *)&server_addr, sizeof(server_addr));
|
||||
if (!ASSERT_EQ(bytes, sizeof(foobar), "send packet"))
|
||||
goto close_client;
|
||||
|
||||
/* Verify we received all expected bytes */
|
||||
bytes = read(sfd, buf, sizeof(buf));
|
||||
if (!ASSERT_EQ(bytes, sizeof(buf), "receive packet"))
|
||||
goto close_client;
|
||||
ASSERT_STREQ(buf, foobar, "check udp packet");
|
||||
|
||||
close_client:
|
||||
close(cfd);
|
||||
close_server:
|
||||
close(sfd);
|
||||
close_netns:
|
||||
close_netns(nstoken);
|
||||
|
||||
out:
|
||||
cleanup();
|
||||
}
|
||||
@@ -33,20 +33,25 @@ void test_netns_cookie(void)
|
||||
|
||||
skel->links.get_netns_cookie_sockops = bpf_program__attach_cgroup(
|
||||
skel->progs.get_netns_cookie_sockops, cgroup_fd);
|
||||
if (!ASSERT_OK_PTR(skel->links.get_netns_cookie_sockops, "prog_attach"))
|
||||
if (!ASSERT_OK_PTR(skel->links.get_netns_cookie_sockops, "prog_attach_sockops"))
|
||||
goto done;
|
||||
|
||||
verdict = bpf_program__fd(skel->progs.get_netns_cookie_sk_msg);
|
||||
map = bpf_map__fd(skel->maps.sock_map);
|
||||
err = bpf_prog_attach(verdict, map, BPF_SK_MSG_VERDICT, 0);
|
||||
if (!ASSERT_OK(err, "prog_attach"))
|
||||
if (!ASSERT_OK(err, "prog_attach_sk_msg"))
|
||||
goto done;
|
||||
|
||||
tc_fd = bpf_program__fd(skel->progs.get_netns_cookie_tcx);
|
||||
err = bpf_prog_attach_opts(tc_fd, loopback, BPF_TCX_INGRESS, &opta);
|
||||
if (!ASSERT_OK(err, "prog_attach"))
|
||||
if (!ASSERT_OK(err, "prog_attach_tcx"))
|
||||
goto done;
|
||||
|
||||
skel->links.get_netns_cookie_cgroup_skb = bpf_program__attach_cgroup(
|
||||
skel->progs.get_netns_cookie_cgroup_skb, cgroup_fd);
|
||||
if (!ASSERT_OK_PTR(skel->links.get_netns_cookie_cgroup_skb, "prog_attach_cgroup_skb"))
|
||||
goto cleanup_tc;
|
||||
|
||||
server_fd = start_server(AF_INET6, SOCK_STREAM, "::1", 0, 0);
|
||||
if (CHECK(server_fd < 0, "start_server", "errno %d\n", errno))
|
||||
goto cleanup_tc;
|
||||
@@ -69,16 +74,18 @@ void test_netns_cookie(void)
|
||||
if (!ASSERT_OK(err, "getsockopt"))
|
||||
goto cleanup_tc;
|
||||
|
||||
ASSERT_EQ(val, cookie_expected_value, "cookie_value");
|
||||
ASSERT_EQ(val, cookie_expected_value, "cookie_value_sockops");
|
||||
|
||||
err = bpf_map_lookup_elem(bpf_map__fd(skel->maps.sk_msg_netns_cookies),
|
||||
&client_fd, &val);
|
||||
if (!ASSERT_OK(err, "map_lookup(sk_msg_netns_cookies)"))
|
||||
goto cleanup_tc;
|
||||
|
||||
ASSERT_EQ(val, cookie_expected_value, "cookie_value");
|
||||
ASSERT_EQ(skel->bss->tcx_init_netns_cookie, cookie_expected_value, "cookie_value");
|
||||
ASSERT_EQ(skel->bss->tcx_netns_cookie, cookie_expected_value, "cookie_value");
|
||||
ASSERT_EQ(val, cookie_expected_value, "cookie_value_sk_msg");
|
||||
ASSERT_EQ(skel->bss->tcx_init_netns_cookie, cookie_expected_value, "cookie_value_init_tcx");
|
||||
ASSERT_EQ(skel->bss->tcx_netns_cookie, cookie_expected_value, "cookie_value_tcx");
|
||||
ASSERT_EQ(skel->bss->cgroup_skb_init_netns_cookie, cookie_expected_value, "cookie_value_init_cgroup_skb");
|
||||
ASSERT_EQ(skel->bss->cgroup_skb_netns_cookie, cookie_expected_value, "cookie_value_cgroup_skb");
|
||||
|
||||
cleanup_tc:
|
||||
err = bpf_prog_detach_opts(tc_fd, loopback, BPF_TCX_INGRESS, &optd);
|
||||
|
||||
@@ -200,41 +200,28 @@ static void test_ns_current_pid_tgid_new_ns(int (*fn)(void *), void *arg)
|
||||
return;
|
||||
}
|
||||
|
||||
static void test_in_netns(int (*fn)(void *), void *arg)
|
||||
{
|
||||
struct nstoken *nstoken = NULL;
|
||||
|
||||
SYS(cleanup, "ip netns add ns_current_pid_tgid");
|
||||
SYS(cleanup, "ip -net ns_current_pid_tgid link set dev lo up");
|
||||
|
||||
nstoken = open_netns("ns_current_pid_tgid");
|
||||
if (!ASSERT_OK_PTR(nstoken, "open_netns"))
|
||||
goto cleanup;
|
||||
|
||||
test_ns_current_pid_tgid_new_ns(fn, arg);
|
||||
|
||||
cleanup:
|
||||
if (nstoken)
|
||||
close_netns(nstoken);
|
||||
SYS_NOFAIL("ip netns del ns_current_pid_tgid");
|
||||
}
|
||||
|
||||
/* TODO: use a different tracepoint */
|
||||
void serial_test_ns_current_pid_tgid(void)
|
||||
void serial_test_current_pid_tgid(void)
|
||||
{
|
||||
if (test__start_subtest("root_ns_tp"))
|
||||
test_current_pid_tgid_tp(NULL);
|
||||
if (test__start_subtest("new_ns_tp"))
|
||||
test_ns_current_pid_tgid_new_ns(test_current_pid_tgid_tp, NULL);
|
||||
if (test__start_subtest("new_ns_cgrp")) {
|
||||
int cgroup_fd = -1;
|
||||
|
||||
cgroup_fd = test__join_cgroup("/sock_addr");
|
||||
if (ASSERT_GE(cgroup_fd, 0, "join_cgroup")) {
|
||||
test_in_netns(test_current_pid_tgid_cgrp, &cgroup_fd);
|
||||
close(cgroup_fd);
|
||||
}
|
||||
}
|
||||
if (test__start_subtest("new_ns_sk_msg"))
|
||||
test_in_netns(test_current_pid_tgid_sk_msg, NULL);
|
||||
}
|
||||
|
||||
void test_ns_current_pid_tgid_cgrp(void)
|
||||
{
|
||||
int cgroup_fd = test__join_cgroup("/sock_addr");
|
||||
|
||||
if (ASSERT_OK_FD(cgroup_fd, "join_cgroup")) {
|
||||
test_ns_current_pid_tgid_new_ns(test_current_pid_tgid_cgrp, &cgroup_fd);
|
||||
close(cgroup_fd);
|
||||
}
|
||||
}
|
||||
|
||||
void test_ns_current_pid_tgid_sk_msg(void)
|
||||
{
|
||||
test_ns_current_pid_tgid_new_ns(test_current_pid_tgid_sk_msg, NULL);
|
||||
}
|
||||
|
||||
|
||||
|
||||
99
tools/testing/selftests/bpf/prog_tests/prepare.c
Normal file
99
tools/testing/selftests/bpf/prog_tests/prepare.c
Normal file
@@ -0,0 +1,99 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2025 Meta */
|
||||
|
||||
#include <test_progs.h>
|
||||
#include <network_helpers.h>
|
||||
#include "prepare.skel.h"
|
||||
|
||||
static bool check_prepared(struct bpf_object *obj)
|
||||
{
|
||||
bool is_prepared = true;
|
||||
const struct bpf_map *map;
|
||||
|
||||
bpf_object__for_each_map(map, obj) {
|
||||
if (bpf_map__fd(map) < 0)
|
||||
is_prepared = false;
|
||||
}
|
||||
|
||||
return is_prepared;
|
||||
}
|
||||
|
||||
static void test_prepare_no_load(void)
|
||||
{
|
||||
struct prepare *skel;
|
||||
int err;
|
||||
LIBBPF_OPTS(bpf_test_run_opts, topts,
|
||||
.data_in = &pkt_v4,
|
||||
.data_size_in = sizeof(pkt_v4),
|
||||
);
|
||||
|
||||
skel = prepare__open();
|
||||
if (!ASSERT_OK_PTR(skel, "prepare__open"))
|
||||
return;
|
||||
|
||||
if (!ASSERT_FALSE(check_prepared(skel->obj), "not check_prepared"))
|
||||
goto cleanup;
|
||||
|
||||
err = bpf_object__prepare(skel->obj);
|
||||
|
||||
if (!ASSERT_TRUE(check_prepared(skel->obj), "check_prepared"))
|
||||
goto cleanup;
|
||||
|
||||
if (!ASSERT_OK(err, "bpf_object__prepare"))
|
||||
goto cleanup;
|
||||
|
||||
cleanup:
|
||||
prepare__destroy(skel);
|
||||
}
|
||||
|
||||
static void test_prepare_load(void)
|
||||
{
|
||||
struct prepare *skel;
|
||||
int err, prog_fd;
|
||||
LIBBPF_OPTS(bpf_test_run_opts, topts,
|
||||
.data_in = &pkt_v4,
|
||||
.data_size_in = sizeof(pkt_v4),
|
||||
);
|
||||
|
||||
skel = prepare__open();
|
||||
if (!ASSERT_OK_PTR(skel, "prepare__open"))
|
||||
return;
|
||||
|
||||
if (!ASSERT_FALSE(check_prepared(skel->obj), "not check_prepared"))
|
||||
goto cleanup;
|
||||
|
||||
err = bpf_object__prepare(skel->obj);
|
||||
if (!ASSERT_OK(err, "bpf_object__prepare"))
|
||||
goto cleanup;
|
||||
|
||||
err = prepare__load(skel);
|
||||
if (!ASSERT_OK(err, "prepare__load"))
|
||||
goto cleanup;
|
||||
|
||||
if (!ASSERT_TRUE(check_prepared(skel->obj), "check_prepared"))
|
||||
goto cleanup;
|
||||
|
||||
prog_fd = bpf_program__fd(skel->progs.program);
|
||||
if (!ASSERT_GE(prog_fd, 0, "prog_fd"))
|
||||
goto cleanup;
|
||||
|
||||
err = bpf_prog_test_run_opts(prog_fd, &topts);
|
||||
if (!ASSERT_OK(err, "test_run_opts err"))
|
||||
goto cleanup;
|
||||
|
||||
if (!ASSERT_OK(topts.retval, "test_run_opts retval"))
|
||||
goto cleanup;
|
||||
|
||||
ASSERT_EQ(skel->bss->err, 0, "err");
|
||||
|
||||
cleanup:
|
||||
prepare__destroy(skel);
|
||||
}
|
||||
|
||||
void test_prepare(void)
|
||||
{
|
||||
if (test__start_subtest("prepare_load"))
|
||||
test_prepare_load();
|
||||
if (test__start_subtest("prepare_no_load"))
|
||||
test_prepare_no_load();
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "epilogue_tailcall.skel.h"
|
||||
#include "pro_epilogue_goto_start.skel.h"
|
||||
#include "epilogue_exit.skel.h"
|
||||
#include "pro_epilogue_with_kfunc.skel.h"
|
||||
|
||||
struct st_ops_args {
|
||||
__u64 a;
|
||||
@@ -55,6 +56,7 @@ void test_pro_epilogue(void)
|
||||
RUN_TESTS(pro_epilogue);
|
||||
RUN_TESTS(pro_epilogue_goto_start);
|
||||
RUN_TESTS(epilogue_exit);
|
||||
RUN_TESTS(pro_epilogue_with_kfunc);
|
||||
if (test__start_subtest("tailcall"))
|
||||
test_tailcall();
|
||||
}
|
||||
|
||||
@@ -81,6 +81,9 @@ static const char * const inproper_region_tests[] = {
|
||||
"nested_rcu_region",
|
||||
"rcu_read_lock_global_subprog_lock",
|
||||
"rcu_read_lock_global_subprog_unlock",
|
||||
"rcu_read_lock_sleepable_helper_global_subprog",
|
||||
"rcu_read_lock_sleepable_kfunc_global_subprog",
|
||||
"rcu_read_lock_sleepable_global_subprog_indirect",
|
||||
};
|
||||
|
||||
static void test_inproper_region(void)
|
||||
|
||||
@@ -24,6 +24,7 @@ struct read_ret_desc {
|
||||
{ .name = "copy_from_user", .ret = -EFAULT },
|
||||
{ .name = "copy_from_user_task", .ret = -EFAULT },
|
||||
{ .name = "copy_from_user_str", .ret = -EFAULT },
|
||||
{ .name = "copy_from_user_task_str", .ret = -EFAULT },
|
||||
};
|
||||
|
||||
void test_read_vsyscall(void)
|
||||
|
||||
@@ -202,7 +202,7 @@ err_out:
|
||||
void test_setget_sockopt(void)
|
||||
{
|
||||
cg_fd = test__join_cgroup(CG_NAME);
|
||||
if (cg_fd < 0)
|
||||
if (!ASSERT_OK_FD(cg_fd, "join cgroup"))
|
||||
return;
|
||||
|
||||
if (create_netns())
|
||||
|
||||
@@ -50,6 +50,9 @@ static struct {
|
||||
{ "lock_id_mismatch_innermapval_mapval", "bpf_spin_unlock of different lock" },
|
||||
{ "lock_global_subprog_call1", "global function calls are not allowed while holding a lock" },
|
||||
{ "lock_global_subprog_call2", "global function calls are not allowed while holding a lock" },
|
||||
{ "lock_global_sleepable_helper_subprog", "global function calls are not allowed while holding a lock" },
|
||||
{ "lock_global_sleepable_kfunc_subprog", "global function calls are not allowed while holding a lock" },
|
||||
{ "lock_global_sleepable_subprog_indirect", "global function calls are not allowed while holding a lock" },
|
||||
};
|
||||
|
||||
static int match_regex(const char *pattern, const char *string)
|
||||
|
||||
144
tools/testing/selftests/bpf/prog_tests/summarization.c
Normal file
144
tools/testing/selftests/bpf/prog_tests/summarization.c
Normal file
@@ -0,0 +1,144 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include "bpf/libbpf.h"
|
||||
#include "summarization_freplace.skel.h"
|
||||
#include "summarization.skel.h"
|
||||
#include <test_progs.h>
|
||||
|
||||
static void print_verifier_log(const char *log)
|
||||
{
|
||||
if (env.verbosity >= VERBOSE_VERY)
|
||||
fprintf(stdout, "VERIFIER LOG:\n=============\n%s=============\n", log);
|
||||
}
|
||||
|
||||
static void test_aux(const char *main_prog_name,
|
||||
const char *to_be_replaced,
|
||||
const char *replacement,
|
||||
bool expect_load,
|
||||
const char *err_msg)
|
||||
{
|
||||
struct summarization_freplace *freplace = NULL;
|
||||
struct bpf_program *freplace_prog = NULL;
|
||||
struct bpf_program *main_prog = NULL;
|
||||
LIBBPF_OPTS(bpf_object_open_opts, opts);
|
||||
struct summarization *main = NULL;
|
||||
char log[16*1024];
|
||||
int err;
|
||||
|
||||
opts.kernel_log_buf = log;
|
||||
opts.kernel_log_size = sizeof(log);
|
||||
if (env.verbosity >= VERBOSE_SUPER)
|
||||
opts.kernel_log_level = 1 | 2 | 4;
|
||||
main = summarization__open_opts(&opts);
|
||||
if (!ASSERT_OK_PTR(main, "summarization__open"))
|
||||
goto out;
|
||||
main_prog = bpf_object__find_program_by_name(main->obj, main_prog_name);
|
||||
if (!ASSERT_OK_PTR(main_prog, "main_prog"))
|
||||
goto out;
|
||||
bpf_program__set_autoload(main_prog, true);
|
||||
err = summarization__load(main);
|
||||
print_verifier_log(log);
|
||||
if (!ASSERT_OK(err, "summarization__load"))
|
||||
goto out;
|
||||
freplace = summarization_freplace__open_opts(&opts);
|
||||
if (!ASSERT_OK_PTR(freplace, "summarization_freplace__open"))
|
||||
goto out;
|
||||
freplace_prog = bpf_object__find_program_by_name(freplace->obj, replacement);
|
||||
if (!ASSERT_OK_PTR(freplace_prog, "freplace_prog"))
|
||||
goto out;
|
||||
bpf_program__set_autoload(freplace_prog, true);
|
||||
bpf_program__set_autoattach(freplace_prog, true);
|
||||
bpf_program__set_attach_target(freplace_prog,
|
||||
bpf_program__fd(main_prog),
|
||||
to_be_replaced);
|
||||
err = summarization_freplace__load(freplace);
|
||||
print_verifier_log(log);
|
||||
|
||||
/* The might_sleep extension doesn't work yet as sleepable calls are not
|
||||
* allowed, but preserve the check in case it's supported later and then
|
||||
* this particular combination can be enabled.
|
||||
*/
|
||||
if (!strcmp("might_sleep", replacement) && err) {
|
||||
ASSERT_HAS_SUBSTR(log, "helper call might sleep in a non-sleepable prog", "error log");
|
||||
ASSERT_EQ(err, -EINVAL, "err");
|
||||
test__skip();
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (expect_load) {
|
||||
ASSERT_OK(err, "summarization_freplace__load");
|
||||
} else {
|
||||
ASSERT_ERR(err, "summarization_freplace__load");
|
||||
ASSERT_HAS_SUBSTR(log, err_msg, "error log");
|
||||
}
|
||||
|
||||
out:
|
||||
summarization_freplace__destroy(freplace);
|
||||
summarization__destroy(main);
|
||||
}
|
||||
|
||||
/* There are two global subprograms in both summarization.skel.h:
|
||||
* - one changes packet data;
|
||||
* - another does not.
|
||||
* It is ok to freplace subprograms that change packet data with those
|
||||
* that either do or do not. It is only ok to freplace subprograms
|
||||
* that do not change packet data with those that do not as well.
|
||||
* The below tests check outcomes for each combination of such freplace.
|
||||
* Also test a case when main subprogram itself is replaced and is a single
|
||||
* subprogram in a program.
|
||||
*
|
||||
* This holds for might_sleep programs. It is ok to replace might_sleep with
|
||||
* might_sleep and with does_not_sleep, but does_not_sleep cannot be replaced
|
||||
* with might_sleep.
|
||||
*/
|
||||
void test_summarization_freplace(void)
|
||||
{
|
||||
struct {
|
||||
const char *main;
|
||||
const char *to_be_replaced;
|
||||
bool has_side_effect;
|
||||
} mains[2][4] = {
|
||||
{
|
||||
{ "main_changes_with_subprogs", "changes_pkt_data", true },
|
||||
{ "main_changes_with_subprogs", "does_not_change_pkt_data", false },
|
||||
{ "main_changes", "main_changes", true },
|
||||
{ "main_does_not_change", "main_does_not_change", false },
|
||||
},
|
||||
{
|
||||
{ "main_might_sleep_with_subprogs", "might_sleep", true },
|
||||
{ "main_might_sleep_with_subprogs", "does_not_sleep", false },
|
||||
{ "main_might_sleep", "main_might_sleep", true },
|
||||
{ "main_does_not_sleep", "main_does_not_sleep", false },
|
||||
},
|
||||
};
|
||||
const char *pkt_err = "Extension program changes packet data";
|
||||
const char *slp_err = "Extension program may sleep";
|
||||
struct {
|
||||
const char *func;
|
||||
bool has_side_effect;
|
||||
const char *err_msg;
|
||||
} replacements[2][2] = {
|
||||
{
|
||||
{ "changes_pkt_data", true, pkt_err },
|
||||
{ "does_not_change_pkt_data", false, pkt_err },
|
||||
},
|
||||
{
|
||||
{ "might_sleep", true, slp_err },
|
||||
{ "does_not_sleep", false, slp_err },
|
||||
},
|
||||
};
|
||||
char buf[64];
|
||||
|
||||
for (int t = 0; t < 2; t++) {
|
||||
for (int i = 0; i < ARRAY_SIZE(mains); ++i) {
|
||||
for (int j = 0; j < ARRAY_SIZE(replacements); ++j) {
|
||||
snprintf(buf, sizeof(buf), "%s_with_%s",
|
||||
mains[t][i].to_be_replaced, replacements[t][j].func);
|
||||
if (!test__start_subtest(buf))
|
||||
continue;
|
||||
test_aux(mains[t][i].main, mains[t][i].to_be_replaced, replacements[t][j].func,
|
||||
mains[t][i].has_side_effect || !replacements[t][j].has_side_effect,
|
||||
replacements[t][j].err_msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1600,6 +1600,7 @@ static void test_tailcall_bpf2bpf_freplace(void)
|
||||
goto out;
|
||||
|
||||
err = bpf_link__destroy(freplace_link);
|
||||
freplace_link = NULL;
|
||||
if (!ASSERT_OK(err, "destroy link"))
|
||||
goto out;
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#include "netlink_helpers.h"
|
||||
#include "tc_helpers.h"
|
||||
|
||||
void serial_test_tc_links_basic(void)
|
||||
void test_ns_tc_links_basic(void)
|
||||
{
|
||||
LIBBPF_OPTS(bpf_prog_query_opts, optq);
|
||||
LIBBPF_OPTS(bpf_tcx_opts, optl);
|
||||
@@ -260,7 +260,7 @@ cleanup:
|
||||
assert_mprog_count(target, 0);
|
||||
}
|
||||
|
||||
void serial_test_tc_links_before(void)
|
||||
void test_ns_tc_links_before(void)
|
||||
{
|
||||
test_tc_links_before_target(BPF_TCX_INGRESS);
|
||||
test_tc_links_before_target(BPF_TCX_EGRESS);
|
||||
@@ -414,7 +414,7 @@ cleanup:
|
||||
assert_mprog_count(target, 0);
|
||||
}
|
||||
|
||||
void serial_test_tc_links_after(void)
|
||||
void test_ns_tc_links_after(void)
|
||||
{
|
||||
test_tc_links_after_target(BPF_TCX_INGRESS);
|
||||
test_tc_links_after_target(BPF_TCX_EGRESS);
|
||||
@@ -514,7 +514,7 @@ cleanup:
|
||||
assert_mprog_count(target, 0);
|
||||
}
|
||||
|
||||
void serial_test_tc_links_revision(void)
|
||||
void test_ns_tc_links_revision(void)
|
||||
{
|
||||
test_tc_links_revision_target(BPF_TCX_INGRESS);
|
||||
test_tc_links_revision_target(BPF_TCX_EGRESS);
|
||||
@@ -618,7 +618,7 @@ cleanup:
|
||||
assert_mprog_count(target, 0);
|
||||
}
|
||||
|
||||
void serial_test_tc_links_chain_classic(void)
|
||||
void test_ns_tc_links_chain_classic(void)
|
||||
{
|
||||
test_tc_chain_classic(BPF_TCX_INGRESS, false);
|
||||
test_tc_chain_classic(BPF_TCX_EGRESS, false);
|
||||
@@ -846,7 +846,7 @@ cleanup:
|
||||
assert_mprog_count(target, 0);
|
||||
}
|
||||
|
||||
void serial_test_tc_links_replace(void)
|
||||
void test_ns_tc_links_replace(void)
|
||||
{
|
||||
test_tc_links_replace_target(BPF_TCX_INGRESS);
|
||||
test_tc_links_replace_target(BPF_TCX_EGRESS);
|
||||
@@ -1158,7 +1158,7 @@ cleanup:
|
||||
assert_mprog_count(target, 0);
|
||||
}
|
||||
|
||||
void serial_test_tc_links_invalid(void)
|
||||
void test_ns_tc_links_invalid(void)
|
||||
{
|
||||
test_tc_links_invalid_target(BPF_TCX_INGRESS);
|
||||
test_tc_links_invalid_target(BPF_TCX_EGRESS);
|
||||
@@ -1314,7 +1314,7 @@ cleanup:
|
||||
assert_mprog_count(target, 0);
|
||||
}
|
||||
|
||||
void serial_test_tc_links_prepend(void)
|
||||
void test_ns_tc_links_prepend(void)
|
||||
{
|
||||
test_tc_links_prepend_target(BPF_TCX_INGRESS);
|
||||
test_tc_links_prepend_target(BPF_TCX_EGRESS);
|
||||
@@ -1470,7 +1470,7 @@ cleanup:
|
||||
assert_mprog_count(target, 0);
|
||||
}
|
||||
|
||||
void serial_test_tc_links_append(void)
|
||||
void test_ns_tc_links_append(void)
|
||||
{
|
||||
test_tc_links_append_target(BPF_TCX_INGRESS);
|
||||
test_tc_links_append_target(BPF_TCX_EGRESS);
|
||||
@@ -1568,7 +1568,7 @@ cleanup:
|
||||
ASSERT_EQ(if_nametoindex("tcx_opts2"), 0, "dev2_removed");
|
||||
}
|
||||
|
||||
void serial_test_tc_links_dev_cleanup(void)
|
||||
void test_ns_tc_links_dev_cleanup(void)
|
||||
{
|
||||
test_tc_links_dev_cleanup_target(BPF_TCX_INGRESS);
|
||||
test_tc_links_dev_cleanup_target(BPF_TCX_EGRESS);
|
||||
@@ -1672,7 +1672,7 @@ cleanup:
|
||||
test_tc_link__destroy(skel);
|
||||
}
|
||||
|
||||
void serial_test_tc_links_chain_mixed(void)
|
||||
void test_ns_tc_links_chain_mixed(void)
|
||||
{
|
||||
test_tc_chain_mixed(BPF_TCX_INGRESS);
|
||||
test_tc_chain_mixed(BPF_TCX_EGRESS);
|
||||
@@ -1782,7 +1782,7 @@ cleanup:
|
||||
assert_mprog_count(target, 0);
|
||||
}
|
||||
|
||||
void serial_test_tc_links_ingress(void)
|
||||
void test_ns_tc_links_ingress(void)
|
||||
{
|
||||
test_tc_links_ingress(BPF_TCX_INGRESS, true, true);
|
||||
test_tc_links_ingress(BPF_TCX_INGRESS, true, false);
|
||||
@@ -1823,7 +1823,7 @@ static int qdisc_replace(int ifindex, const char *kind, bool block)
|
||||
return err;
|
||||
}
|
||||
|
||||
void serial_test_tc_links_dev_chain0(void)
|
||||
void test_ns_tc_links_dev_chain0(void)
|
||||
{
|
||||
int err, ifindex;
|
||||
|
||||
@@ -1955,7 +1955,7 @@ cleanup:
|
||||
ASSERT_EQ(if_nametoindex("tcx_opts2"), 0, "dev2_removed");
|
||||
}
|
||||
|
||||
void serial_test_tc_links_dev_mixed(void)
|
||||
void test_ns_tc_links_dev_mixed(void)
|
||||
{
|
||||
test_tc_links_dev_mixed(BPF_TCX_INGRESS);
|
||||
test_tc_links_dev_mixed(BPF_TCX_EGRESS);
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#include "test_tc_link.skel.h"
|
||||
#include "tc_helpers.h"
|
||||
|
||||
void serial_test_tc_opts_basic(void)
|
||||
void test_ns_tc_opts_basic(void)
|
||||
{
|
||||
LIBBPF_OPTS(bpf_prog_attach_opts, opta);
|
||||
LIBBPF_OPTS(bpf_prog_detach_opts, optd);
|
||||
@@ -254,7 +254,7 @@ cleanup:
|
||||
test_tc_link__destroy(skel);
|
||||
}
|
||||
|
||||
void serial_test_tc_opts_before(void)
|
||||
void test_ns_tc_opts_before(void)
|
||||
{
|
||||
test_tc_opts_before_target(BPF_TCX_INGRESS);
|
||||
test_tc_opts_before_target(BPF_TCX_EGRESS);
|
||||
@@ -445,7 +445,7 @@ cleanup:
|
||||
test_tc_link__destroy(skel);
|
||||
}
|
||||
|
||||
void serial_test_tc_opts_after(void)
|
||||
void test_ns_tc_opts_after(void)
|
||||
{
|
||||
test_tc_opts_after_target(BPF_TCX_INGRESS);
|
||||
test_tc_opts_after_target(BPF_TCX_EGRESS);
|
||||
@@ -554,7 +554,7 @@ cleanup:
|
||||
test_tc_link__destroy(skel);
|
||||
}
|
||||
|
||||
void serial_test_tc_opts_revision(void)
|
||||
void test_ns_tc_opts_revision(void)
|
||||
{
|
||||
test_tc_opts_revision_target(BPF_TCX_INGRESS);
|
||||
test_tc_opts_revision_target(BPF_TCX_EGRESS);
|
||||
@@ -655,7 +655,7 @@ cleanup:
|
||||
assert_mprog_count(target, 0);
|
||||
}
|
||||
|
||||
void serial_test_tc_opts_chain_classic(void)
|
||||
void test_ns_tc_opts_chain_classic(void)
|
||||
{
|
||||
test_tc_chain_classic(BPF_TCX_INGRESS, false);
|
||||
test_tc_chain_classic(BPF_TCX_EGRESS, false);
|
||||
@@ -864,7 +864,7 @@ cleanup:
|
||||
test_tc_link__destroy(skel);
|
||||
}
|
||||
|
||||
void serial_test_tc_opts_replace(void)
|
||||
void test_ns_tc_opts_replace(void)
|
||||
{
|
||||
test_tc_opts_replace_target(BPF_TCX_INGRESS);
|
||||
test_tc_opts_replace_target(BPF_TCX_EGRESS);
|
||||
@@ -1017,7 +1017,7 @@ cleanup:
|
||||
test_tc_link__destroy(skel);
|
||||
}
|
||||
|
||||
void serial_test_tc_opts_invalid(void)
|
||||
void test_ns_tc_opts_invalid(void)
|
||||
{
|
||||
test_tc_opts_invalid_target(BPF_TCX_INGRESS);
|
||||
test_tc_opts_invalid_target(BPF_TCX_EGRESS);
|
||||
@@ -1157,7 +1157,7 @@ cleanup:
|
||||
test_tc_link__destroy(skel);
|
||||
}
|
||||
|
||||
void serial_test_tc_opts_prepend(void)
|
||||
void test_ns_tc_opts_prepend(void)
|
||||
{
|
||||
test_tc_opts_prepend_target(BPF_TCX_INGRESS);
|
||||
test_tc_opts_prepend_target(BPF_TCX_EGRESS);
|
||||
@@ -1297,7 +1297,7 @@ cleanup:
|
||||
test_tc_link__destroy(skel);
|
||||
}
|
||||
|
||||
void serial_test_tc_opts_append(void)
|
||||
void test_ns_tc_opts_append(void)
|
||||
{
|
||||
test_tc_opts_append_target(BPF_TCX_INGRESS);
|
||||
test_tc_opts_append_target(BPF_TCX_EGRESS);
|
||||
@@ -1387,7 +1387,7 @@ cleanup:
|
||||
ASSERT_EQ(if_nametoindex("tcx_opts2"), 0, "dev2_removed");
|
||||
}
|
||||
|
||||
void serial_test_tc_opts_dev_cleanup(void)
|
||||
void test_ns_tc_opts_dev_cleanup(void)
|
||||
{
|
||||
test_tc_opts_dev_cleanup_target(BPF_TCX_INGRESS);
|
||||
test_tc_opts_dev_cleanup_target(BPF_TCX_EGRESS);
|
||||
@@ -1563,7 +1563,7 @@ cleanup:
|
||||
assert_mprog_count(target, 0);
|
||||
}
|
||||
|
||||
void serial_test_tc_opts_mixed(void)
|
||||
void test_ns_tc_opts_mixed(void)
|
||||
{
|
||||
test_tc_opts_mixed_target(BPF_TCX_INGRESS);
|
||||
test_tc_opts_mixed_target(BPF_TCX_EGRESS);
|
||||
@@ -1642,7 +1642,7 @@ cleanup:
|
||||
assert_mprog_count(target, 0);
|
||||
}
|
||||
|
||||
void serial_test_tc_opts_demixed(void)
|
||||
void test_ns_tc_opts_demixed(void)
|
||||
{
|
||||
test_tc_opts_demixed_target(BPF_TCX_INGRESS);
|
||||
test_tc_opts_demixed_target(BPF_TCX_EGRESS);
|
||||
@@ -1813,7 +1813,7 @@ cleanup:
|
||||
test_tc_link__destroy(skel);
|
||||
}
|
||||
|
||||
void serial_test_tc_opts_detach(void)
|
||||
void test_ns_tc_opts_detach(void)
|
||||
{
|
||||
test_tc_opts_detach_target(BPF_TCX_INGRESS);
|
||||
test_tc_opts_detach_target(BPF_TCX_EGRESS);
|
||||
@@ -2020,7 +2020,7 @@ cleanup:
|
||||
test_tc_link__destroy(skel);
|
||||
}
|
||||
|
||||
void serial_test_tc_opts_detach_before(void)
|
||||
void test_ns_tc_opts_detach_before(void)
|
||||
{
|
||||
test_tc_opts_detach_before_target(BPF_TCX_INGRESS);
|
||||
test_tc_opts_detach_before_target(BPF_TCX_EGRESS);
|
||||
@@ -2236,7 +2236,7 @@ cleanup:
|
||||
test_tc_link__destroy(skel);
|
||||
}
|
||||
|
||||
void serial_test_tc_opts_detach_after(void)
|
||||
void test_ns_tc_opts_detach_after(void)
|
||||
{
|
||||
test_tc_opts_detach_after_target(BPF_TCX_INGRESS);
|
||||
test_tc_opts_detach_after_target(BPF_TCX_EGRESS);
|
||||
@@ -2265,7 +2265,7 @@ static void test_tc_opts_delete_empty(int target, bool chain_tc_old)
|
||||
assert_mprog_count(target, 0);
|
||||
}
|
||||
|
||||
void serial_test_tc_opts_delete_empty(void)
|
||||
void test_ns_tc_opts_delete_empty(void)
|
||||
{
|
||||
test_tc_opts_delete_empty(BPF_TCX_INGRESS, false);
|
||||
test_tc_opts_delete_empty(BPF_TCX_EGRESS, false);
|
||||
@@ -2372,7 +2372,7 @@ cleanup:
|
||||
test_tc_link__destroy(skel);
|
||||
}
|
||||
|
||||
void serial_test_tc_opts_chain_mixed(void)
|
||||
void test_ns_tc_opts_chain_mixed(void)
|
||||
{
|
||||
test_tc_chain_mixed(BPF_TCX_INGRESS);
|
||||
test_tc_chain_mixed(BPF_TCX_EGRESS);
|
||||
@@ -2446,7 +2446,7 @@ cleanup:
|
||||
ASSERT_EQ(if_nametoindex("tcx_opts2"), 0, "dev2_removed");
|
||||
}
|
||||
|
||||
void serial_test_tc_opts_max(void)
|
||||
void test_ns_tc_opts_max(void)
|
||||
{
|
||||
test_tc_opts_max_target(BPF_TCX_INGRESS, 0, false);
|
||||
test_tc_opts_max_target(BPF_TCX_EGRESS, 0, false);
|
||||
@@ -2748,7 +2748,7 @@ cleanup:
|
||||
test_tc_link__destroy(skel);
|
||||
}
|
||||
|
||||
void serial_test_tc_opts_query(void)
|
||||
void test_ns_tc_opts_query(void)
|
||||
{
|
||||
test_tc_opts_query_target(BPF_TCX_INGRESS);
|
||||
test_tc_opts_query_target(BPF_TCX_EGRESS);
|
||||
@@ -2807,7 +2807,7 @@ cleanup:
|
||||
test_tc_link__destroy(skel);
|
||||
}
|
||||
|
||||
void serial_test_tc_opts_query_attach(void)
|
||||
void test_ns_tc_opts_query_attach(void)
|
||||
{
|
||||
test_tc_opts_query_attach_target(BPF_TCX_INGRESS);
|
||||
test_tc_opts_query_attach_target(BPF_TCX_EGRESS);
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
#include <test_progs.h>
|
||||
|
||||
#include "struct_ops_kptr_return.skel.h"
|
||||
#include "struct_ops_kptr_return_fail__wrong_type.skel.h"
|
||||
#include "struct_ops_kptr_return_fail__invalid_scalar.skel.h"
|
||||
#include "struct_ops_kptr_return_fail__nonzero_offset.skel.h"
|
||||
#include "struct_ops_kptr_return_fail__local_kptr.skel.h"
|
||||
|
||||
void test_struct_ops_kptr_return(void)
|
||||
{
|
||||
RUN_TESTS(struct_ops_kptr_return);
|
||||
RUN_TESTS(struct_ops_kptr_return_fail__wrong_type);
|
||||
RUN_TESTS(struct_ops_kptr_return_fail__invalid_scalar);
|
||||
RUN_TESTS(struct_ops_kptr_return_fail__nonzero_offset);
|
||||
RUN_TESTS(struct_ops_kptr_return_fail__local_kptr);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#include <test_progs.h>
|
||||
|
||||
#include "struct_ops_refcounted.skel.h"
|
||||
#include "struct_ops_refcounted_fail__ref_leak.skel.h"
|
||||
#include "struct_ops_refcounted_fail__global_subprog.skel.h"
|
||||
#include "struct_ops_refcounted_fail__tail_call.skel.h"
|
||||
|
||||
void test_struct_ops_refcounted(void)
|
||||
{
|
||||
RUN_TESTS(struct_ops_refcounted);
|
||||
RUN_TESTS(struct_ops_refcounted_fail__ref_leak);
|
||||
RUN_TESTS(struct_ops_refcounted_fail__global_subprog);
|
||||
RUN_TESTS(struct_ops_refcounted_fail__tail_call);
|
||||
}
|
||||
@@ -71,6 +71,8 @@
|
||||
#define IP4_ADDR2_VETH1 "172.16.1.20"
|
||||
#define IP4_ADDR_TUNL_DEV0 "10.1.1.100"
|
||||
#define IP4_ADDR_TUNL_DEV1 "10.1.1.200"
|
||||
#define IP6_ADDR_TUNL_DEV0 "fc80::100"
|
||||
#define IP6_ADDR_TUNL_DEV1 "fc80::200"
|
||||
|
||||
#define IP6_ADDR_VETH0 "::11"
|
||||
#define IP6_ADDR1_VETH1 "::22"
|
||||
@@ -98,6 +100,27 @@
|
||||
#define XFRM_SPI_IN_TO_OUT 0x1
|
||||
#define XFRM_SPI_OUT_TO_IN 0x2
|
||||
|
||||
#define GRE_TUNL_DEV0 "gre00"
|
||||
#define GRE_TUNL_DEV1 "gre11"
|
||||
|
||||
#define IP6GRE_TUNL_DEV0 "ip6gre00"
|
||||
#define IP6GRE_TUNL_DEV1 "ip6gre11"
|
||||
|
||||
#define ERSPAN_TUNL_DEV0 "erspan00"
|
||||
#define ERSPAN_TUNL_DEV1 "erspan11"
|
||||
|
||||
#define IP6ERSPAN_TUNL_DEV0 "ip6erspan00"
|
||||
#define IP6ERSPAN_TUNL_DEV1 "ip6erspan11"
|
||||
|
||||
#define GENEVE_TUNL_DEV0 "geneve00"
|
||||
#define GENEVE_TUNL_DEV1 "geneve11"
|
||||
|
||||
#define IP6GENEVE_TUNL_DEV0 "ip6geneve00"
|
||||
#define IP6GENEVE_TUNL_DEV1 "ip6geneve11"
|
||||
|
||||
#define IP6TNL_TUNL_DEV0 "ip6tnl00"
|
||||
#define IP6TNL_TUNL_DEV1 "ip6tnl11"
|
||||
|
||||
#define PING_ARGS "-i 0.01 -c 3 -w 10 -q"
|
||||
|
||||
static int config_device(void)
|
||||
@@ -216,6 +239,18 @@ fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int set_ipv4_addr(const char *dev0, const char *dev1)
|
||||
{
|
||||
SYS(fail, "ip -n at_ns0 link set dev %s up", dev0);
|
||||
SYS(fail, "ip -n at_ns0 addr add dev %s %s/24", dev0, IP4_ADDR_TUNL_DEV0);
|
||||
SYS(fail, "ip link set dev %s up", dev1);
|
||||
SYS(fail, "ip addr add dev %s %s/24", dev1, IP4_ADDR_TUNL_DEV1);
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int add_ipip_tunnel(enum ipip_encap encap)
|
||||
{
|
||||
int err;
|
||||
@@ -356,6 +391,99 @@ static void delete_xfrm_tunnel(void)
|
||||
IP4_ADDR1_VETH1, IP4_ADDR_VETH0, XFRM_SPI_OUT_TO_IN);
|
||||
}
|
||||
|
||||
static int add_ipv4_tunnel(const char *dev0, const char *dev1,
|
||||
const char *type, const char *opt)
|
||||
{
|
||||
if (!type || !opt || !dev0 || !dev1)
|
||||
return -1;
|
||||
|
||||
SYS(fail, "ip -n at_ns0 link add dev %s type %s %s local %s remote %s",
|
||||
dev0, type, opt, IP4_ADDR_VETH0, IP4_ADDR1_VETH1);
|
||||
|
||||
SYS(fail, "ip link add dev %s type %s external", dev1, type);
|
||||
|
||||
return set_ipv4_addr(dev0, dev1);
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void delete_tunnel(const char *dev0, const char *dev1)
|
||||
{
|
||||
if (!dev0 || !dev1)
|
||||
return;
|
||||
|
||||
SYS_NOFAIL("ip netns exec at_ns0 ip link delete dev %s", dev0);
|
||||
SYS_NOFAIL("ip link delete dev %s", dev1);
|
||||
}
|
||||
|
||||
static int set_ipv6_addr(const char *dev0, const char *dev1)
|
||||
{
|
||||
/* disable IPv6 DAD because it might take too long and fail tests */
|
||||
SYS(fail, "ip -n at_ns0 addr add %s/96 dev veth0 nodad", IP6_ADDR_VETH0);
|
||||
SYS(fail, "ip -n at_ns0 link set dev veth0 up");
|
||||
SYS(fail, "ip addr add %s/96 dev veth1 nodad", IP6_ADDR1_VETH1);
|
||||
SYS(fail, "ip link set dev veth1 up");
|
||||
|
||||
SYS(fail, "ip -n at_ns0 addr add dev %s %s/24", dev0, IP4_ADDR_TUNL_DEV0);
|
||||
SYS(fail, "ip -n at_ns0 addr add dev %s %s/96 nodad", dev0, IP6_ADDR_TUNL_DEV0);
|
||||
SYS(fail, "ip -n at_ns0 link set dev %s up", dev0);
|
||||
|
||||
SYS(fail, "ip addr add dev %s %s/24", dev1, IP4_ADDR_TUNL_DEV1);
|
||||
SYS(fail, "ip addr add dev %s %s/96 nodad", dev1, IP6_ADDR_TUNL_DEV1);
|
||||
SYS(fail, "ip link set dev %s up", dev1);
|
||||
return 0;
|
||||
fail:
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int add_ipv6_tunnel(const char *dev0, const char *dev1,
|
||||
const char *type, const char *opt)
|
||||
{
|
||||
if (!type || !opt || !dev0 || !dev1)
|
||||
return -1;
|
||||
|
||||
SYS(fail, "ip -n at_ns0 link add dev %s type %s %s local %s remote %s",
|
||||
dev0, type, opt, IP6_ADDR_VETH0, IP6_ADDR1_VETH1);
|
||||
|
||||
SYS(fail, "ip link add dev %s type %s external", dev1, type);
|
||||
|
||||
return set_ipv6_addr(dev0, dev1);
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int add_geneve_tunnel(const char *dev0, const char *dev1,
|
||||
const char *type, const char *opt)
|
||||
{
|
||||
if (!type || !opt || !dev0 || !dev1)
|
||||
return -1;
|
||||
|
||||
SYS(fail, "ip -n at_ns0 link add dev %s type %s id 2 %s remote %s",
|
||||
dev0, type, opt, IP4_ADDR1_VETH1);
|
||||
|
||||
SYS(fail, "ip link add dev %s type %s %s external", dev1, type, opt);
|
||||
|
||||
return set_ipv4_addr(dev0, dev1);
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int add_ip6geneve_tunnel(const char *dev0, const char *dev1,
|
||||
const char *type, const char *opt)
|
||||
{
|
||||
if (!type || !opt || !dev0 || !dev1)
|
||||
return -1;
|
||||
|
||||
SYS(fail, "ip -n at_ns0 link add dev %s type %s id 22 %s remote %s",
|
||||
dev0, type, opt, IP6_ADDR1_VETH1);
|
||||
|
||||
SYS(fail, "ip link add dev %s type %s %s external", dev1, type, opt);
|
||||
|
||||
return set_ipv6_addr(dev0, dev1);
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int test_ping(int family, const char *addr)
|
||||
{
|
||||
SYS(fail, "%s %s %s > /dev/null", ping_command(family), PING_ARGS, addr);
|
||||
@@ -364,32 +492,76 @@ fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int attach_tc_prog(struct bpf_tc_hook *hook, int igr_fd, int egr_fd)
|
||||
static void ping_dev0(void)
|
||||
{
|
||||
/* ping from root namespace test */
|
||||
test_ping(AF_INET, IP4_ADDR_TUNL_DEV0);
|
||||
}
|
||||
|
||||
static void ping_dev1(void)
|
||||
{
|
||||
struct nstoken *nstoken;
|
||||
|
||||
/* ping from at_ns0 namespace test */
|
||||
nstoken = open_netns("at_ns0");
|
||||
if (!ASSERT_OK_PTR(nstoken, "setns"))
|
||||
return;
|
||||
|
||||
test_ping(AF_INET, IP4_ADDR_TUNL_DEV1);
|
||||
close_netns(nstoken);
|
||||
}
|
||||
|
||||
static void ping6_veth0(void)
|
||||
{
|
||||
test_ping(AF_INET6, IP6_ADDR_VETH0);
|
||||
}
|
||||
|
||||
static void ping6_dev0(void)
|
||||
{
|
||||
test_ping(AF_INET6, IP6_ADDR_TUNL_DEV0);
|
||||
}
|
||||
|
||||
static void ping6_dev1(void)
|
||||
{
|
||||
struct nstoken *nstoken;
|
||||
|
||||
/* ping from at_ns0 namespace test */
|
||||
nstoken = open_netns("at_ns0");
|
||||
if (!ASSERT_OK_PTR(nstoken, "setns"))
|
||||
return;
|
||||
|
||||
test_ping(AF_INET, IP6_ADDR_TUNL_DEV1);
|
||||
close_netns(nstoken);
|
||||
}
|
||||
|
||||
static int attach_tc_prog(int ifindex, int igr_fd, int egr_fd)
|
||||
{
|
||||
DECLARE_LIBBPF_OPTS(bpf_tc_hook, hook, .ifindex = ifindex,
|
||||
.attach_point = BPF_TC_INGRESS | BPF_TC_EGRESS);
|
||||
DECLARE_LIBBPF_OPTS(bpf_tc_opts, opts1, .handle = 1,
|
||||
.priority = 1, .prog_fd = igr_fd);
|
||||
DECLARE_LIBBPF_OPTS(bpf_tc_opts, opts2, .handle = 1,
|
||||
.priority = 1, .prog_fd = egr_fd);
|
||||
int ret;
|
||||
|
||||
ret = bpf_tc_hook_create(hook);
|
||||
ret = bpf_tc_hook_create(&hook);
|
||||
if (!ASSERT_OK(ret, "create tc hook"))
|
||||
return ret;
|
||||
|
||||
if (igr_fd >= 0) {
|
||||
hook->attach_point = BPF_TC_INGRESS;
|
||||
ret = bpf_tc_attach(hook, &opts1);
|
||||
hook.attach_point = BPF_TC_INGRESS;
|
||||
ret = bpf_tc_attach(&hook, &opts1);
|
||||
if (!ASSERT_OK(ret, "bpf_tc_attach")) {
|
||||
bpf_tc_hook_destroy(hook);
|
||||
bpf_tc_hook_destroy(&hook);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (egr_fd >= 0) {
|
||||
hook->attach_point = BPF_TC_EGRESS;
|
||||
ret = bpf_tc_attach(hook, &opts2);
|
||||
hook.attach_point = BPF_TC_EGRESS;
|
||||
ret = bpf_tc_attach(&hook, &opts2);
|
||||
if (!ASSERT_OK(ret, "bpf_tc_attach")) {
|
||||
bpf_tc_hook_destroy(hook);
|
||||
bpf_tc_hook_destroy(&hook);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
@@ -397,6 +569,50 @@ static int attach_tc_prog(struct bpf_tc_hook *hook, int igr_fd, int egr_fd)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int generic_attach(const char *dev, int igr_fd, int egr_fd)
|
||||
{
|
||||
int ifindex;
|
||||
|
||||
if (!ASSERT_OK_FD(igr_fd, "check ingress fd"))
|
||||
return -1;
|
||||
if (!ASSERT_OK_FD(egr_fd, "check egress fd"))
|
||||
return -1;
|
||||
|
||||
ifindex = if_nametoindex(dev);
|
||||
if (!ASSERT_NEQ(ifindex, 0, "get ifindex"))
|
||||
return -1;
|
||||
|
||||
return attach_tc_prog(ifindex, igr_fd, egr_fd);
|
||||
}
|
||||
|
||||
static int generic_attach_igr(const char *dev, int igr_fd)
|
||||
{
|
||||
int ifindex;
|
||||
|
||||
if (!ASSERT_OK_FD(igr_fd, "check ingress fd"))
|
||||
return -1;
|
||||
|
||||
ifindex = if_nametoindex(dev);
|
||||
if (!ASSERT_NEQ(ifindex, 0, "get ifindex"))
|
||||
return -1;
|
||||
|
||||
return attach_tc_prog(ifindex, igr_fd, -1);
|
||||
}
|
||||
|
||||
static int generic_attach_egr(const char *dev, int egr_fd)
|
||||
{
|
||||
int ifindex;
|
||||
|
||||
if (!ASSERT_OK_FD(egr_fd, "check egress fd"))
|
||||
return -1;
|
||||
|
||||
ifindex = if_nametoindex(dev);
|
||||
if (!ASSERT_NEQ(ifindex, 0, "get ifindex"))
|
||||
return -1;
|
||||
|
||||
return attach_tc_prog(ifindex, -1, egr_fd);
|
||||
}
|
||||
|
||||
static void test_vxlan_tunnel(void)
|
||||
{
|
||||
struct test_tunnel_kern *skel = NULL;
|
||||
@@ -404,11 +620,9 @@ static void test_vxlan_tunnel(void)
|
||||
int local_ip_map_fd = -1;
|
||||
int set_src_prog_fd, get_src_prog_fd;
|
||||
int set_dst_prog_fd;
|
||||
int key = 0, ifindex = -1;
|
||||
int key = 0;
|
||||
uint local_ip;
|
||||
int err;
|
||||
DECLARE_LIBBPF_OPTS(bpf_tc_hook, tc_hook,
|
||||
.attach_point = BPF_TC_INGRESS);
|
||||
|
||||
/* add vxlan tunnel */
|
||||
err = add_vxlan_tunnel();
|
||||
@@ -419,42 +633,22 @@ static void test_vxlan_tunnel(void)
|
||||
skel = test_tunnel_kern__open_and_load();
|
||||
if (!ASSERT_OK_PTR(skel, "test_tunnel_kern__open_and_load"))
|
||||
goto done;
|
||||
ifindex = if_nametoindex(VXLAN_TUNL_DEV1);
|
||||
if (!ASSERT_NEQ(ifindex, 0, "vxlan11 ifindex"))
|
||||
goto done;
|
||||
tc_hook.ifindex = ifindex;
|
||||
get_src_prog_fd = bpf_program__fd(skel->progs.vxlan_get_tunnel_src);
|
||||
set_src_prog_fd = bpf_program__fd(skel->progs.vxlan_set_tunnel_src);
|
||||
if (!ASSERT_GE(get_src_prog_fd, 0, "bpf_program__fd"))
|
||||
goto done;
|
||||
if (!ASSERT_GE(set_src_prog_fd, 0, "bpf_program__fd"))
|
||||
goto done;
|
||||
if (attach_tc_prog(&tc_hook, get_src_prog_fd, set_src_prog_fd))
|
||||
if (generic_attach(VXLAN_TUNL_DEV1, get_src_prog_fd, set_src_prog_fd))
|
||||
goto done;
|
||||
|
||||
/* load and attach bpf prog to veth dev tc hook point */
|
||||
ifindex = if_nametoindex("veth1");
|
||||
if (!ASSERT_NEQ(ifindex, 0, "veth1 ifindex"))
|
||||
goto done;
|
||||
tc_hook.ifindex = ifindex;
|
||||
set_dst_prog_fd = bpf_program__fd(skel->progs.veth_set_outer_dst);
|
||||
if (!ASSERT_GE(set_dst_prog_fd, 0, "bpf_program__fd"))
|
||||
goto done;
|
||||
if (attach_tc_prog(&tc_hook, set_dst_prog_fd, -1))
|
||||
if (generic_attach_igr("veth1", set_dst_prog_fd))
|
||||
goto done;
|
||||
|
||||
/* load and attach prog set_md to tunnel dev tc hook point at_ns0 */
|
||||
nstoken = open_netns("at_ns0");
|
||||
if (!ASSERT_OK_PTR(nstoken, "setns src"))
|
||||
goto done;
|
||||
ifindex = if_nametoindex(VXLAN_TUNL_DEV0);
|
||||
if (!ASSERT_NEQ(ifindex, 0, "vxlan00 ifindex"))
|
||||
goto done;
|
||||
tc_hook.ifindex = ifindex;
|
||||
set_dst_prog_fd = bpf_program__fd(skel->progs.vxlan_set_tunnel_dst);
|
||||
if (!ASSERT_GE(set_dst_prog_fd, 0, "bpf_program__fd"))
|
||||
goto done;
|
||||
if (attach_tc_prog(&tc_hook, -1, set_dst_prog_fd))
|
||||
if (generic_attach_egr(VXLAN_TUNL_DEV0, set_dst_prog_fd))
|
||||
goto done;
|
||||
close_netns(nstoken);
|
||||
|
||||
@@ -468,9 +662,7 @@ static void test_vxlan_tunnel(void)
|
||||
goto done;
|
||||
|
||||
/* ping test */
|
||||
err = test_ping(AF_INET, IP4_ADDR_TUNL_DEV0);
|
||||
if (!ASSERT_OK(err, "test_ping"))
|
||||
goto done;
|
||||
ping_dev0();
|
||||
|
||||
done:
|
||||
/* delete vxlan tunnel */
|
||||
@@ -488,11 +680,9 @@ static void test_ip6vxlan_tunnel(void)
|
||||
int local_ip_map_fd = -1;
|
||||
int set_src_prog_fd, get_src_prog_fd;
|
||||
int set_dst_prog_fd;
|
||||
int key = 0, ifindex = -1;
|
||||
int key = 0;
|
||||
uint local_ip;
|
||||
int err;
|
||||
DECLARE_LIBBPF_OPTS(bpf_tc_hook, tc_hook,
|
||||
.attach_point = BPF_TC_INGRESS);
|
||||
|
||||
/* add vxlan tunnel */
|
||||
err = add_ip6vxlan_tunnel();
|
||||
@@ -503,31 +693,17 @@ static void test_ip6vxlan_tunnel(void)
|
||||
skel = test_tunnel_kern__open_and_load();
|
||||
if (!ASSERT_OK_PTR(skel, "test_tunnel_kern__open_and_load"))
|
||||
goto done;
|
||||
ifindex = if_nametoindex(IP6VXLAN_TUNL_DEV1);
|
||||
if (!ASSERT_NEQ(ifindex, 0, "ip6vxlan11 ifindex"))
|
||||
goto done;
|
||||
tc_hook.ifindex = ifindex;
|
||||
get_src_prog_fd = bpf_program__fd(skel->progs.ip6vxlan_get_tunnel_src);
|
||||
set_src_prog_fd = bpf_program__fd(skel->progs.ip6vxlan_set_tunnel_src);
|
||||
if (!ASSERT_GE(set_src_prog_fd, 0, "bpf_program__fd"))
|
||||
goto done;
|
||||
if (!ASSERT_GE(get_src_prog_fd, 0, "bpf_program__fd"))
|
||||
goto done;
|
||||
if (attach_tc_prog(&tc_hook, get_src_prog_fd, set_src_prog_fd))
|
||||
if (generic_attach(IP6VXLAN_TUNL_DEV1, get_src_prog_fd, set_src_prog_fd))
|
||||
goto done;
|
||||
|
||||
/* load and attach prog set_md to tunnel dev tc hook point at_ns0 */
|
||||
nstoken = open_netns("at_ns0");
|
||||
if (!ASSERT_OK_PTR(nstoken, "setns src"))
|
||||
goto done;
|
||||
ifindex = if_nametoindex(IP6VXLAN_TUNL_DEV0);
|
||||
if (!ASSERT_NEQ(ifindex, 0, "ip6vxlan00 ifindex"))
|
||||
goto done;
|
||||
tc_hook.ifindex = ifindex;
|
||||
set_dst_prog_fd = bpf_program__fd(skel->progs.ip6vxlan_set_tunnel_dst);
|
||||
if (!ASSERT_GE(set_dst_prog_fd, 0, "bpf_program__fd"))
|
||||
goto done;
|
||||
if (attach_tc_prog(&tc_hook, -1, set_dst_prog_fd))
|
||||
if (generic_attach_egr(IP6VXLAN_TUNL_DEV0, set_dst_prog_fd))
|
||||
goto done;
|
||||
close_netns(nstoken);
|
||||
|
||||
@@ -541,9 +717,7 @@ static void test_ip6vxlan_tunnel(void)
|
||||
goto done;
|
||||
|
||||
/* ping test */
|
||||
err = test_ping(AF_INET, IP4_ADDR_TUNL_DEV0);
|
||||
if (!ASSERT_OK(err, "test_ping"))
|
||||
goto done;
|
||||
ping_dev0();
|
||||
|
||||
done:
|
||||
/* delete ipv6 vxlan tunnel */
|
||||
@@ -557,12 +731,8 @@ done:
|
||||
static void test_ipip_tunnel(enum ipip_encap encap)
|
||||
{
|
||||
struct test_tunnel_kern *skel = NULL;
|
||||
struct nstoken *nstoken;
|
||||
int set_src_prog_fd, get_src_prog_fd;
|
||||
int ifindex = -1;
|
||||
int err;
|
||||
DECLARE_LIBBPF_OPTS(bpf_tc_hook, tc_hook,
|
||||
.attach_point = BPF_TC_INGRESS);
|
||||
|
||||
/* add ipip tunnel */
|
||||
err = add_ipip_tunnel(encap);
|
||||
@@ -573,10 +743,6 @@ static void test_ipip_tunnel(enum ipip_encap encap)
|
||||
skel = test_tunnel_kern__open_and_load();
|
||||
if (!ASSERT_OK_PTR(skel, "test_tunnel_kern__open_and_load"))
|
||||
goto done;
|
||||
ifindex = if_nametoindex(IPIP_TUNL_DEV1);
|
||||
if (!ASSERT_NEQ(ifindex, 0, "ipip11 ifindex"))
|
||||
goto done;
|
||||
tc_hook.ifindex = ifindex;
|
||||
|
||||
switch (encap) {
|
||||
case FOU:
|
||||
@@ -598,26 +764,11 @@ static void test_ipip_tunnel(enum ipip_encap encap)
|
||||
skel->progs.ipip_set_tunnel);
|
||||
}
|
||||
|
||||
if (!ASSERT_GE(set_src_prog_fd, 0, "bpf_program__fd"))
|
||||
goto done;
|
||||
if (!ASSERT_GE(get_src_prog_fd, 0, "bpf_program__fd"))
|
||||
goto done;
|
||||
if (attach_tc_prog(&tc_hook, get_src_prog_fd, set_src_prog_fd))
|
||||
if (generic_attach(IPIP_TUNL_DEV1, get_src_prog_fd, set_src_prog_fd))
|
||||
goto done;
|
||||
|
||||
/* ping from root namespace test */
|
||||
err = test_ping(AF_INET, IP4_ADDR_TUNL_DEV0);
|
||||
if (!ASSERT_OK(err, "test_ping"))
|
||||
goto done;
|
||||
|
||||
/* ping from at_ns0 namespace test */
|
||||
nstoken = open_netns("at_ns0");
|
||||
if (!ASSERT_OK_PTR(nstoken, "setns"))
|
||||
goto done;
|
||||
err = test_ping(AF_INET, IP4_ADDR_TUNL_DEV1);
|
||||
if (!ASSERT_OK(err, "test_ping"))
|
||||
goto done;
|
||||
close_netns(nstoken);
|
||||
ping_dev0();
|
||||
ping_dev1();
|
||||
|
||||
done:
|
||||
/* delete ipip tunnel */
|
||||
@@ -628,11 +779,8 @@ done:
|
||||
|
||||
static void test_xfrm_tunnel(void)
|
||||
{
|
||||
DECLARE_LIBBPF_OPTS(bpf_tc_hook, tc_hook,
|
||||
.attach_point = BPF_TC_INGRESS);
|
||||
LIBBPF_OPTS(bpf_xdp_attach_opts, opts);
|
||||
struct test_tunnel_kern *skel = NULL;
|
||||
struct nstoken *nstoken;
|
||||
int xdp_prog_fd;
|
||||
int tc_prog_fd;
|
||||
int ifindex;
|
||||
@@ -646,19 +794,16 @@ static void test_xfrm_tunnel(void)
|
||||
if (!ASSERT_OK_PTR(skel, "test_tunnel_kern__open_and_load"))
|
||||
goto done;
|
||||
|
||||
ifindex = if_nametoindex("veth1");
|
||||
if (!ASSERT_NEQ(ifindex, 0, "veth1 ifindex"))
|
||||
goto done;
|
||||
|
||||
/* attach tc prog to tunnel dev */
|
||||
tc_hook.ifindex = ifindex;
|
||||
tc_prog_fd = bpf_program__fd(skel->progs.xfrm_get_state);
|
||||
if (!ASSERT_GE(tc_prog_fd, 0, "bpf_program__fd"))
|
||||
goto done;
|
||||
if (attach_tc_prog(&tc_hook, tc_prog_fd, -1))
|
||||
if (generic_attach_igr("veth1", tc_prog_fd))
|
||||
goto done;
|
||||
|
||||
/* attach xdp prog to tunnel dev */
|
||||
ifindex = if_nametoindex("veth1");
|
||||
if (!ASSERT_NEQ(ifindex, 0, "veth1 ifindex"))
|
||||
goto done;
|
||||
xdp_prog_fd = bpf_program__fd(skel->progs.xfrm_get_state_xdp);
|
||||
if (!ASSERT_GE(xdp_prog_fd, 0, "bpf_program__fd"))
|
||||
goto done;
|
||||
@@ -666,14 +811,7 @@ static void test_xfrm_tunnel(void)
|
||||
if (!ASSERT_OK(err, "bpf_xdp_attach"))
|
||||
goto done;
|
||||
|
||||
/* ping from at_ns0 namespace test */
|
||||
nstoken = open_netns("at_ns0");
|
||||
if (!ASSERT_OK_PTR(nstoken, "setns"))
|
||||
goto done;
|
||||
err = test_ping(AF_INET, IP4_ADDR_TUNL_DEV1);
|
||||
close_netns(nstoken);
|
||||
if (!ASSERT_OK(err, "test_ping"))
|
||||
goto done;
|
||||
ping_dev1();
|
||||
|
||||
if (!ASSERT_EQ(skel->bss->xfrm_reqid, 1, "req_id"))
|
||||
goto done;
|
||||
@@ -690,6 +828,281 @@ done:
|
||||
test_tunnel_kern__destroy(skel);
|
||||
}
|
||||
|
||||
enum gre_test {
|
||||
GRE,
|
||||
GRE_NOKEY,
|
||||
GRETAP,
|
||||
GRETAP_NOKEY,
|
||||
};
|
||||
|
||||
static void test_gre_tunnel(enum gre_test test)
|
||||
{
|
||||
struct test_tunnel_kern *skel;
|
||||
int set_fd, get_fd;
|
||||
int err;
|
||||
|
||||
skel = test_tunnel_kern__open_and_load();
|
||||
if (!ASSERT_OK_PTR(skel, "test_tunnel_kern__open_and_load"))
|
||||
return;
|
||||
|
||||
switch (test) {
|
||||
case GRE:
|
||||
err = add_ipv4_tunnel(GRE_TUNL_DEV0, GRE_TUNL_DEV1, "gre", "seq");
|
||||
set_fd = bpf_program__fd(skel->progs.gre_set_tunnel_no_key);
|
||||
get_fd = bpf_program__fd(skel->progs.gre_get_tunnel);
|
||||
break;
|
||||
case GRE_NOKEY:
|
||||
err = add_ipv4_tunnel(GRE_TUNL_DEV0, GRE_TUNL_DEV1, "gre", "seq key 2");
|
||||
set_fd = bpf_program__fd(skel->progs.gre_set_tunnel);
|
||||
get_fd = bpf_program__fd(skel->progs.gre_get_tunnel);
|
||||
break;
|
||||
case GRETAP:
|
||||
err = add_ipv4_tunnel(GRE_TUNL_DEV0, GRE_TUNL_DEV1, "gretap", "seq");
|
||||
set_fd = bpf_program__fd(skel->progs.gre_set_tunnel_no_key);
|
||||
get_fd = bpf_program__fd(skel->progs.gre_get_tunnel);
|
||||
break;
|
||||
case GRETAP_NOKEY:
|
||||
err = add_ipv4_tunnel(GRE_TUNL_DEV0, GRE_TUNL_DEV1, "gretap", "seq key 2");
|
||||
set_fd = bpf_program__fd(skel->progs.gre_set_tunnel);
|
||||
get_fd = bpf_program__fd(skel->progs.gre_get_tunnel);
|
||||
break;
|
||||
}
|
||||
if (!ASSERT_OK(err, "add tunnel"))
|
||||
goto done;
|
||||
|
||||
if (generic_attach(GRE_TUNL_DEV1, get_fd, set_fd))
|
||||
goto done;
|
||||
|
||||
ping_dev0();
|
||||
ping_dev1();
|
||||
|
||||
done:
|
||||
delete_tunnel(GRE_TUNL_DEV0, GRE_TUNL_DEV1);
|
||||
test_tunnel_kern__destroy(skel);
|
||||
}
|
||||
|
||||
enum ip6gre_test {
|
||||
IP6GRE,
|
||||
IP6GRETAP
|
||||
};
|
||||
|
||||
static void test_ip6gre_tunnel(enum ip6gre_test test)
|
||||
{
|
||||
struct test_tunnel_kern *skel;
|
||||
int set_fd, get_fd;
|
||||
int err;
|
||||
|
||||
skel = test_tunnel_kern__open_and_load();
|
||||
if (!ASSERT_OK_PTR(skel, "test_tunnel_kern__open_and_load"))
|
||||
return;
|
||||
|
||||
switch (test) {
|
||||
case IP6GRE:
|
||||
err = add_ipv6_tunnel(IP6GRE_TUNL_DEV0, IP6GRE_TUNL_DEV1,
|
||||
"ip6gre", "flowlabel 0xbcdef key 2");
|
||||
break;
|
||||
case IP6GRETAP:
|
||||
err = add_ipv6_tunnel(IP6GRE_TUNL_DEV0, IP6GRE_TUNL_DEV1,
|
||||
"ip6gretap", "flowlabel 0xbcdef key 2");
|
||||
break;
|
||||
}
|
||||
if (!ASSERT_OK(err, "add tunnel"))
|
||||
goto done;
|
||||
|
||||
set_fd = bpf_program__fd(skel->progs.ip6gretap_set_tunnel);
|
||||
get_fd = bpf_program__fd(skel->progs.ip6gretap_get_tunnel);
|
||||
if (generic_attach(IP6GRE_TUNL_DEV1, get_fd, set_fd))
|
||||
goto done;
|
||||
|
||||
ping6_veth0();
|
||||
ping6_dev1();
|
||||
ping_dev0();
|
||||
ping_dev1();
|
||||
done:
|
||||
delete_tunnel(IP6GRE_TUNL_DEV0, IP6GRE_TUNL_DEV1);
|
||||
test_tunnel_kern__destroy(skel);
|
||||
}
|
||||
|
||||
enum erspan_test {
|
||||
V1,
|
||||
V2
|
||||
};
|
||||
|
||||
static void test_erspan_tunnel(enum erspan_test test)
|
||||
{
|
||||
struct test_tunnel_kern *skel;
|
||||
int set_fd, get_fd;
|
||||
int err;
|
||||
|
||||
skel = test_tunnel_kern__open_and_load();
|
||||
if (!ASSERT_OK_PTR(skel, "test_tunnel_kern__open_and_load"))
|
||||
return;
|
||||
|
||||
switch (test) {
|
||||
case V1:
|
||||
err = add_ipv4_tunnel(ERSPAN_TUNL_DEV0, ERSPAN_TUNL_DEV1,
|
||||
"erspan", "seq key 2 erspan_ver 1 erspan 123");
|
||||
break;
|
||||
case V2:
|
||||
err = add_ipv4_tunnel(ERSPAN_TUNL_DEV0, ERSPAN_TUNL_DEV1,
|
||||
"erspan",
|
||||
"seq key 2 erspan_ver 2 erspan_dir egress erspan_hwid 3");
|
||||
break;
|
||||
}
|
||||
if (!ASSERT_OK(err, "add tunnel"))
|
||||
goto done;
|
||||
|
||||
set_fd = bpf_program__fd(skel->progs.erspan_set_tunnel);
|
||||
get_fd = bpf_program__fd(skel->progs.erspan_get_tunnel);
|
||||
if (generic_attach(ERSPAN_TUNL_DEV1, get_fd, set_fd))
|
||||
goto done;
|
||||
|
||||
ping_dev0();
|
||||
ping_dev1();
|
||||
done:
|
||||
delete_tunnel(ERSPAN_TUNL_DEV0, ERSPAN_TUNL_DEV1);
|
||||
test_tunnel_kern__destroy(skel);
|
||||
}
|
||||
|
||||
static void test_ip6erspan_tunnel(enum erspan_test test)
|
||||
{
|
||||
struct test_tunnel_kern *skel;
|
||||
int set_fd, get_fd;
|
||||
int err;
|
||||
|
||||
skel = test_tunnel_kern__open_and_load();
|
||||
if (!ASSERT_OK_PTR(skel, "test_tunnel_kern__open_and_load"))
|
||||
return;
|
||||
|
||||
switch (test) {
|
||||
case V1:
|
||||
err = add_ipv6_tunnel(IP6ERSPAN_TUNL_DEV0, IP6ERSPAN_TUNL_DEV1,
|
||||
"ip6erspan", "seq key 2 erspan_ver 1 erspan 123");
|
||||
break;
|
||||
case V2:
|
||||
err = add_ipv6_tunnel(IP6ERSPAN_TUNL_DEV0, IP6ERSPAN_TUNL_DEV1,
|
||||
"ip6erspan",
|
||||
"seq key 2 erspan_ver 2 erspan_dir egress erspan_hwid 7");
|
||||
break;
|
||||
}
|
||||
if (!ASSERT_OK(err, "add tunnel"))
|
||||
goto done;
|
||||
|
||||
set_fd = bpf_program__fd(skel->progs.ip4ip6erspan_set_tunnel);
|
||||
get_fd = bpf_program__fd(skel->progs.ip4ip6erspan_get_tunnel);
|
||||
if (generic_attach(IP6ERSPAN_TUNL_DEV1, get_fd, set_fd))
|
||||
goto done;
|
||||
|
||||
ping6_veth0();
|
||||
ping_dev1();
|
||||
done:
|
||||
delete_tunnel(IP6ERSPAN_TUNL_DEV0, IP6ERSPAN_TUNL_DEV1);
|
||||
test_tunnel_kern__destroy(skel);
|
||||
}
|
||||
|
||||
static void test_geneve_tunnel(void)
|
||||
{
|
||||
struct test_tunnel_kern *skel;
|
||||
int set_fd, get_fd;
|
||||
int err;
|
||||
|
||||
skel = test_tunnel_kern__open_and_load();
|
||||
if (!ASSERT_OK_PTR(skel, "test_tunnel_kern__open_and_load"))
|
||||
return;
|
||||
|
||||
err = add_geneve_tunnel(GENEVE_TUNL_DEV0, GENEVE_TUNL_DEV1,
|
||||
"geneve", "dstport 6081");
|
||||
if (!ASSERT_OK(err, "add tunnel"))
|
||||
goto done;
|
||||
|
||||
set_fd = bpf_program__fd(skel->progs.geneve_set_tunnel);
|
||||
get_fd = bpf_program__fd(skel->progs.geneve_get_tunnel);
|
||||
if (generic_attach(GENEVE_TUNL_DEV1, get_fd, set_fd))
|
||||
goto done;
|
||||
|
||||
ping_dev0();
|
||||
ping_dev1();
|
||||
done:
|
||||
delete_tunnel(GENEVE_TUNL_DEV0, GENEVE_TUNL_DEV1);
|
||||
test_tunnel_kern__destroy(skel);
|
||||
}
|
||||
|
||||
static void test_ip6geneve_tunnel(void)
|
||||
{
|
||||
struct test_tunnel_kern *skel;
|
||||
int set_fd, get_fd;
|
||||
int err;
|
||||
|
||||
skel = test_tunnel_kern__open_and_load();
|
||||
if (!ASSERT_OK_PTR(skel, "test_tunnel_kern__open_and_load"))
|
||||
return;
|
||||
|
||||
err = add_ip6geneve_tunnel(IP6GENEVE_TUNL_DEV0, IP6GENEVE_TUNL_DEV1,
|
||||
"geneve", "");
|
||||
if (!ASSERT_OK(err, "add tunnel"))
|
||||
goto done;
|
||||
|
||||
set_fd = bpf_program__fd(skel->progs.ip6geneve_set_tunnel);
|
||||
get_fd = bpf_program__fd(skel->progs.ip6geneve_get_tunnel);
|
||||
if (generic_attach(IP6GENEVE_TUNL_DEV1, get_fd, set_fd))
|
||||
goto done;
|
||||
|
||||
ping_dev0();
|
||||
ping_dev1();
|
||||
done:
|
||||
delete_tunnel(IP6GENEVE_TUNL_DEV0, IP6GENEVE_TUNL_DEV1);
|
||||
test_tunnel_kern__destroy(skel);
|
||||
}
|
||||
|
||||
enum ip6tnl_test {
|
||||
IPIP6,
|
||||
IP6IP6
|
||||
};
|
||||
|
||||
static void test_ip6tnl_tunnel(enum ip6tnl_test test)
|
||||
{
|
||||
struct test_tunnel_kern *skel;
|
||||
int set_fd, get_fd;
|
||||
int err;
|
||||
|
||||
skel = test_tunnel_kern__open_and_load();
|
||||
if (!ASSERT_OK_PTR(skel, "test_tunnel_kern__open_and_load"))
|
||||
return;
|
||||
|
||||
err = add_ipv6_tunnel(IP6TNL_TUNL_DEV0, IP6TNL_TUNL_DEV1, "ip6tnl", "");
|
||||
if (!ASSERT_OK(err, "add tunnel"))
|
||||
goto done;
|
||||
|
||||
switch (test) {
|
||||
case IPIP6:
|
||||
set_fd = bpf_program__fd(skel->progs.ipip6_set_tunnel);
|
||||
get_fd = bpf_program__fd(skel->progs.ipip6_get_tunnel);
|
||||
break;
|
||||
case IP6IP6:
|
||||
set_fd = bpf_program__fd(skel->progs.ip6ip6_set_tunnel);
|
||||
get_fd = bpf_program__fd(skel->progs.ip6ip6_get_tunnel);
|
||||
break;
|
||||
}
|
||||
if (generic_attach(IP6TNL_TUNL_DEV1, get_fd, set_fd))
|
||||
goto done;
|
||||
|
||||
ping6_veth0();
|
||||
switch (test) {
|
||||
case IPIP6:
|
||||
ping_dev0();
|
||||
ping_dev1();
|
||||
break;
|
||||
case IP6IP6:
|
||||
ping6_dev0();
|
||||
ping6_dev1();
|
||||
break;
|
||||
}
|
||||
|
||||
done:
|
||||
delete_tunnel(IP6TNL_TUNL_DEV0, IP6TNL_TUNL_DEV1);
|
||||
test_tunnel_kern__destroy(skel);
|
||||
}
|
||||
|
||||
#define RUN_TEST(name, ...) \
|
||||
({ \
|
||||
if (test__start_subtest(#name)) { \
|
||||
@@ -707,6 +1120,20 @@ static void *test_tunnel_run_tests(void *arg)
|
||||
RUN_TEST(ipip_tunnel, FOU);
|
||||
RUN_TEST(ipip_tunnel, GUE);
|
||||
RUN_TEST(xfrm_tunnel);
|
||||
RUN_TEST(gre_tunnel, GRE);
|
||||
RUN_TEST(gre_tunnel, GRE_NOKEY);
|
||||
RUN_TEST(gre_tunnel, GRETAP);
|
||||
RUN_TEST(gre_tunnel, GRETAP_NOKEY);
|
||||
RUN_TEST(ip6gre_tunnel, IP6GRE);
|
||||
RUN_TEST(ip6gre_tunnel, IP6GRETAP);
|
||||
RUN_TEST(erspan_tunnel, V1);
|
||||
RUN_TEST(erspan_tunnel, V2);
|
||||
RUN_TEST(ip6erspan_tunnel, V1);
|
||||
RUN_TEST(ip6erspan_tunnel, V2);
|
||||
RUN_TEST(geneve_tunnel);
|
||||
RUN_TEST(ip6geneve_tunnel);
|
||||
RUN_TEST(ip6tnl_tunnel, IPIP6);
|
||||
RUN_TEST(ip6tnl_tunnel, IP6IP6);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
139
tools/testing/selftests/bpf/prog_tests/test_veristat.c
Normal file
139
tools/testing/selftests/bpf/prog_tests/test_veristat.c
Normal file
@@ -0,0 +1,139 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */
|
||||
#include <test_progs.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define __CHECK_STR(str, name) \
|
||||
do { \
|
||||
if (!ASSERT_HAS_SUBSTR(fix->output, (str), (name))) \
|
||||
goto out; \
|
||||
} while (0)
|
||||
|
||||
struct fixture {
|
||||
char tmpfile[80];
|
||||
int fd;
|
||||
char *output;
|
||||
size_t sz;
|
||||
char veristat[80];
|
||||
};
|
||||
|
||||
static struct fixture *init_fixture(void)
|
||||
{
|
||||
struct fixture *fix = malloc(sizeof(struct fixture));
|
||||
|
||||
/* for no_alu32 and cpuv4 veristat is in parent folder */
|
||||
if (access("./veristat", F_OK) == 0)
|
||||
strcpy(fix->veristat, "./veristat");
|
||||
else if (access("../veristat", F_OK) == 0)
|
||||
strcpy(fix->veristat, "../veristat");
|
||||
else
|
||||
PRINT_FAIL("Can't find veristat binary");
|
||||
|
||||
snprintf(fix->tmpfile, sizeof(fix->tmpfile), "/tmp/test_veristat.XXXXXX");
|
||||
fix->fd = mkstemp(fix->tmpfile);
|
||||
fix->sz = 1000000;
|
||||
fix->output = malloc(fix->sz);
|
||||
return fix;
|
||||
}
|
||||
|
||||
static void teardown_fixture(struct fixture *fix)
|
||||
{
|
||||
free(fix->output);
|
||||
close(fix->fd);
|
||||
remove(fix->tmpfile);
|
||||
free(fix);
|
||||
}
|
||||
|
||||
static void test_set_global_vars_succeeds(void)
|
||||
{
|
||||
struct fixture *fix = init_fixture();
|
||||
|
||||
SYS(out,
|
||||
"%s set_global_vars.bpf.o"\
|
||||
" -G \"var_s64 = 0xf000000000000001\" "\
|
||||
" -G \"var_u64 = 0xfedcba9876543210\" "\
|
||||
" -G \"var_s32 = -0x80000000\" "\
|
||||
" -G \"var_u32 = 0x76543210\" "\
|
||||
" -G \"var_s16 = -32768\" "\
|
||||
" -G \"var_u16 = 60652\" "\
|
||||
" -G \"var_s8 = -128\" "\
|
||||
" -G \"var_u8 = 255\" "\
|
||||
" -G \"var_ea = EA2\" "\
|
||||
" -G \"var_eb = EB2\" "\
|
||||
" -G \"var_ec = EC2\" "\
|
||||
" -G \"var_b = 1\" "\
|
||||
"-vl2 > %s", fix->veristat, fix->tmpfile);
|
||||
|
||||
read(fix->fd, fix->output, fix->sz);
|
||||
__CHECK_STR("_w=0xf000000000000001 ", "var_s64 = 0xf000000000000001");
|
||||
__CHECK_STR("_w=0xfedcba9876543210 ", "var_u64 = 0xfedcba9876543210");
|
||||
__CHECK_STR("_w=0x80000000 ", "var_s32 = -0x80000000");
|
||||
__CHECK_STR("_w=0x76543210 ", "var_u32 = 0x76543210");
|
||||
__CHECK_STR("_w=0x8000 ", "var_s16 = -32768");
|
||||
__CHECK_STR("_w=0xecec ", "var_u16 = 60652");
|
||||
__CHECK_STR("_w=128 ", "var_s8 = -128");
|
||||
__CHECK_STR("_w=255 ", "var_u8 = 255");
|
||||
__CHECK_STR("_w=11 ", "var_ea = EA2");
|
||||
__CHECK_STR("_w=12 ", "var_eb = EB2");
|
||||
__CHECK_STR("_w=13 ", "var_ec = EC2");
|
||||
__CHECK_STR("_w=1 ", "var_b = 1");
|
||||
|
||||
out:
|
||||
teardown_fixture(fix);
|
||||
}
|
||||
|
||||
static void test_set_global_vars_from_file_succeeds(void)
|
||||
{
|
||||
struct fixture *fix = init_fixture();
|
||||
char input_file[80];
|
||||
const char *vars = "var_s16 = -32768\nvar_u16 = 60652";
|
||||
int fd;
|
||||
|
||||
snprintf(input_file, sizeof(input_file), "/tmp/veristat_input.XXXXXX");
|
||||
fd = mkstemp(input_file);
|
||||
if (!ASSERT_GE(fd, 0, "valid fd"))
|
||||
goto out;
|
||||
|
||||
write(fd, vars, strlen(vars));
|
||||
syncfs(fd);
|
||||
SYS(out, "%s set_global_vars.bpf.o -G \"@%s\" -vl2 > %s",
|
||||
fix->veristat, input_file, fix->tmpfile);
|
||||
read(fix->fd, fix->output, fix->sz);
|
||||
__CHECK_STR("_w=0x8000 ", "var_s16 = -32768");
|
||||
__CHECK_STR("_w=0xecec ", "var_u16 = 60652");
|
||||
|
||||
out:
|
||||
close(fd);
|
||||
remove(input_file);
|
||||
teardown_fixture(fix);
|
||||
}
|
||||
|
||||
static void test_set_global_vars_out_of_range(void)
|
||||
{
|
||||
struct fixture *fix = init_fixture();
|
||||
|
||||
SYS_FAIL(out,
|
||||
"%s set_global_vars.bpf.o -G \"var_s32 = 2147483648\" -vl2 2> %s",
|
||||
fix->veristat, fix->tmpfile);
|
||||
|
||||
read(fix->fd, fix->output, fix->sz);
|
||||
__CHECK_STR("is out of range [-2147483648; 2147483647]", "out of range");
|
||||
|
||||
out:
|
||||
teardown_fixture(fix);
|
||||
}
|
||||
|
||||
void test_veristat(void)
|
||||
{
|
||||
if (test__start_subtest("set_global_vars_succeeds"))
|
||||
test_set_global_vars_succeeds();
|
||||
|
||||
if (test__start_subtest("set_global_vars_out_of_range"))
|
||||
test_set_global_vars_out_of_range();
|
||||
|
||||
if (test__start_subtest("set_global_vars_from_file_succeeds"))
|
||||
test_set_global_vars_from_file_succeeds();
|
||||
}
|
||||
|
||||
#undef __CHECK_STR
|
||||
@@ -3,17 +3,50 @@
|
||||
/* Create 3 namespaces with 3 veth peers, and forward packets in-between using
|
||||
* native XDP
|
||||
*
|
||||
* XDP_TX
|
||||
* NS1(veth11) NS2(veth22) NS3(veth33)
|
||||
* | | |
|
||||
* | | |
|
||||
* (veth1, (veth2, (veth3,
|
||||
* id:111) id:122) id:133)
|
||||
* ^ | ^ | ^ |
|
||||
* | | XDP_REDIRECT | | XDP_REDIRECT | |
|
||||
* | ------------------ ------------------ |
|
||||
* -----------------------------------------
|
||||
* XDP_REDIRECT
|
||||
* Network topology:
|
||||
* ---------- ---------- ----------
|
||||
* | NS1 | | NS2 | | NS3 |
|
||||
* | veth11 | | veth22 | | veth33 |
|
||||
* ----|----- -----|---- -----|----
|
||||
* | | |
|
||||
* ----|------------------|----------------|----
|
||||
* | veth1 veth2 veth3 |
|
||||
* | |
|
||||
* | NSO |
|
||||
* ---------------------------------------------
|
||||
*
|
||||
* Test cases:
|
||||
* - [test_xdp_veth_redirect] : ping veth33 from veth11
|
||||
*
|
||||
* veth11 veth22 veth33
|
||||
* (XDP_PASS) (XDP_TX) (XDP_PASS)
|
||||
* | | |
|
||||
* | | |
|
||||
* veth1 veth2 veth3
|
||||
* (XDP_REDIRECT) (XDP_REDIRECT) (XDP_REDIRECT)
|
||||
* ^ | ^ | ^ |
|
||||
* | | | | | |
|
||||
* | ------------------ ------------------ |
|
||||
* -----------------------------------------
|
||||
*
|
||||
* - [test_xdp_veth_broadcast_redirect]: broadcast from veth11
|
||||
* - IPv4 ping : BPF_F_BROADCAST | BPF_F_EXCLUDE_INGRESS
|
||||
* -> echo request received by all except veth11
|
||||
* - IPv4 ping : BPF_F_BROADCAST
|
||||
* -> echo request received by all veth
|
||||
* - [test_xdp_veth_egress]:
|
||||
* - all src mac should be the magic mac
|
||||
*
|
||||
* veth11 veth22 veth33
|
||||
* (XDP_PASS) (XDP_PASS) (XDP_PASS)
|
||||
* | | |
|
||||
* | | |
|
||||
* veth1 veth2 veth3
|
||||
* (XDP_REDIRECT) (XDP_REDIRECT) (XDP_REDIRECT)
|
||||
* | ^ ^
|
||||
* | | |
|
||||
* ----------------------------------------
|
||||
*
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
@@ -22,192 +55,545 @@
|
||||
#include "network_helpers.h"
|
||||
#include "xdp_dummy.skel.h"
|
||||
#include "xdp_redirect_map.skel.h"
|
||||
#include "xdp_redirect_multi_kern.skel.h"
|
||||
#include "xdp_tx.skel.h"
|
||||
#include <uapi/linux/if_link.h>
|
||||
|
||||
#define VETH_PAIRS_COUNT 3
|
||||
#define NS_SUFFIX_LEN 6
|
||||
#define VETH_NAME_MAX_LEN 16
|
||||
#define VETH_NAME_MAX_LEN 32
|
||||
#define IP_MAX_LEN 16
|
||||
#define IP_SRC "10.1.1.11"
|
||||
#define IP_DST "10.1.1.33"
|
||||
#define IP_CMD_MAX_LEN 128
|
||||
|
||||
struct skeletons {
|
||||
struct xdp_dummy *xdp_dummy;
|
||||
struct xdp_tx *xdp_tx;
|
||||
struct xdp_redirect_map *xdp_redirect_maps;
|
||||
};
|
||||
#define IP_NEIGH "10.1.1.253"
|
||||
#define PROG_NAME_MAX_LEN 128
|
||||
#define NS_NAME_MAX_LEN 32
|
||||
|
||||
struct veth_configuration {
|
||||
char local_veth[VETH_NAME_MAX_LEN]; /* Interface in main namespace */
|
||||
char remote_veth[VETH_NAME_MAX_LEN]; /* Peer interface in dedicated namespace*/
|
||||
const char *namespace; /* Namespace for the remote veth */
|
||||
char next_veth[VETH_NAME_MAX_LEN]; /* Local interface to redirect traffic to */
|
||||
char *remote_addr; /* IP address of the remote veth */
|
||||
char namespace[NS_NAME_MAX_LEN]; /* Namespace for the remote veth */
|
||||
int next_veth; /* Local interface to redirect traffic to */
|
||||
char remote_addr[IP_MAX_LEN]; /* IP address of the remote veth */
|
||||
};
|
||||
|
||||
static struct veth_configuration config[VETH_PAIRS_COUNT] = {
|
||||
struct net_configuration {
|
||||
char ns0_name[NS_NAME_MAX_LEN];
|
||||
struct veth_configuration veth_cfg[VETH_PAIRS_COUNT];
|
||||
};
|
||||
|
||||
static const struct net_configuration default_config = {
|
||||
.ns0_name = "ns0-",
|
||||
{
|
||||
.local_veth = "veth1",
|
||||
.remote_veth = "veth11",
|
||||
.next_veth = "veth2",
|
||||
.remote_addr = IP_SRC,
|
||||
.namespace = "ns-veth11"
|
||||
},
|
||||
{
|
||||
.local_veth = "veth2",
|
||||
.remote_veth = "veth22",
|
||||
.next_veth = "veth3",
|
||||
.remote_addr = NULL,
|
||||
.namespace = "ns-veth22"
|
||||
},
|
||||
{
|
||||
.local_veth = "veth3",
|
||||
.remote_veth = "veth33",
|
||||
.next_veth = "veth1",
|
||||
.remote_addr = IP_DST,
|
||||
.namespace = "ns-veth33"
|
||||
{
|
||||
.local_veth = "veth1-",
|
||||
.remote_veth = "veth11",
|
||||
.next_veth = 1,
|
||||
.remote_addr = IP_SRC,
|
||||
.namespace = "ns-veth11-"
|
||||
},
|
||||
{
|
||||
.local_veth = "veth2-",
|
||||
.remote_veth = "veth22",
|
||||
.next_veth = 2,
|
||||
.remote_addr = "",
|
||||
.namespace = "ns-veth22-"
|
||||
},
|
||||
{
|
||||
.local_veth = "veth3-",
|
||||
.remote_veth = "veth33",
|
||||
.next_veth = 0,
|
||||
.remote_addr = IP_DST,
|
||||
.namespace = "ns-veth33-"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static int attach_programs_to_veth_pair(struct skeletons *skeletons, int index)
|
||||
struct prog_configuration {
|
||||
char local_name[PROG_NAME_MAX_LEN]; /* BPF prog to attach to local_veth */
|
||||
char remote_name[PROG_NAME_MAX_LEN]; /* BPF prog to attach to remote_veth */
|
||||
u32 local_flags; /* XDP flags to use on local_veth */
|
||||
u32 remote_flags; /* XDP flags to use on remote_veth */
|
||||
};
|
||||
|
||||
static int attach_programs_to_veth_pair(struct bpf_object **objs, size_t nb_obj,
|
||||
struct net_configuration *net_config,
|
||||
struct prog_configuration *prog, int index)
|
||||
{
|
||||
struct bpf_program *local_prog, *remote_prog;
|
||||
struct bpf_link **local_link, **remote_link;
|
||||
struct nstoken *nstoken;
|
||||
struct bpf_link *link;
|
||||
int interface;
|
||||
int interface, ret, i;
|
||||
|
||||
switch (index) {
|
||||
case 0:
|
||||
local_prog = skeletons->xdp_redirect_maps->progs.xdp_redirect_map_0;
|
||||
local_link = &skeletons->xdp_redirect_maps->links.xdp_redirect_map_0;
|
||||
remote_prog = skeletons->xdp_dummy->progs.xdp_dummy_prog;
|
||||
remote_link = &skeletons->xdp_dummy->links.xdp_dummy_prog;
|
||||
break;
|
||||
case 1:
|
||||
local_prog = skeletons->xdp_redirect_maps->progs.xdp_redirect_map_1;
|
||||
local_link = &skeletons->xdp_redirect_maps->links.xdp_redirect_map_1;
|
||||
remote_prog = skeletons->xdp_tx->progs.xdp_tx;
|
||||
remote_link = &skeletons->xdp_tx->links.xdp_tx;
|
||||
break;
|
||||
case 2:
|
||||
local_prog = skeletons->xdp_redirect_maps->progs.xdp_redirect_map_2;
|
||||
local_link = &skeletons->xdp_redirect_maps->links.xdp_redirect_map_2;
|
||||
remote_prog = skeletons->xdp_dummy->progs.xdp_dummy_prog;
|
||||
remote_link = &skeletons->xdp_dummy->links.xdp_dummy_prog;
|
||||
break;
|
||||
for (i = 0; i < nb_obj; i++) {
|
||||
local_prog = bpf_object__find_program_by_name(objs[i], prog[index].local_name);
|
||||
if (local_prog)
|
||||
break;
|
||||
}
|
||||
interface = if_nametoindex(config[index].local_veth);
|
||||
if (!ASSERT_OK_PTR(local_prog, "find local program"))
|
||||
return -1;
|
||||
|
||||
for (i = 0; i < nb_obj; i++) {
|
||||
remote_prog = bpf_object__find_program_by_name(objs[i], prog[index].remote_name);
|
||||
if (remote_prog)
|
||||
break;
|
||||
}
|
||||
if (!ASSERT_OK_PTR(remote_prog, "find remote program"))
|
||||
return -1;
|
||||
|
||||
interface = if_nametoindex(net_config->veth_cfg[index].local_veth);
|
||||
if (!ASSERT_NEQ(interface, 0, "non zero interface index"))
|
||||
return -1;
|
||||
link = bpf_program__attach_xdp(local_prog, interface);
|
||||
if (!ASSERT_OK_PTR(link, "attach xdp program to local veth"))
|
||||
|
||||
ret = bpf_xdp_attach(interface, bpf_program__fd(local_prog),
|
||||
prog[index].local_flags, NULL);
|
||||
if (!ASSERT_OK(ret, "attach xdp program to local veth"))
|
||||
return -1;
|
||||
*local_link = link;
|
||||
nstoken = open_netns(config[index].namespace);
|
||||
|
||||
nstoken = open_netns(net_config->veth_cfg[index].namespace);
|
||||
if (!ASSERT_OK_PTR(nstoken, "switch to remote veth namespace"))
|
||||
return -1;
|
||||
interface = if_nametoindex(config[index].remote_veth);
|
||||
|
||||
interface = if_nametoindex(net_config->veth_cfg[index].remote_veth);
|
||||
if (!ASSERT_NEQ(interface, 0, "non zero interface index")) {
|
||||
close_netns(nstoken);
|
||||
return -1;
|
||||
}
|
||||
link = bpf_program__attach_xdp(remote_prog, interface);
|
||||
*remote_link = link;
|
||||
close_netns(nstoken);
|
||||
if (!ASSERT_OK_PTR(link, "attach xdp program to remote veth"))
|
||||
return -1;
|
||||
|
||||
ret = bpf_xdp_attach(interface, bpf_program__fd(remote_prog),
|
||||
prog[index].remote_flags, NULL);
|
||||
if (!ASSERT_OK(ret, "attach xdp program to remote veth")) {
|
||||
close_netns(nstoken);
|
||||
return -1;
|
||||
}
|
||||
|
||||
close_netns(nstoken);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int configure_network(struct skeletons *skeletons)
|
||||
static int create_network(struct net_configuration *net_config)
|
||||
{
|
||||
int interface_id;
|
||||
int map_fd;
|
||||
int err;
|
||||
int i = 0;
|
||||
struct nstoken *nstoken = NULL;
|
||||
int i, err;
|
||||
|
||||
/* First create and configure all interfaces */
|
||||
for (i = 0; i < VETH_PAIRS_COUNT; i++) {
|
||||
SYS(fail, "ip netns add %s", config[i].namespace);
|
||||
SYS(fail, "ip link add %s type veth peer name %s netns %s",
|
||||
config[i].local_veth, config[i].remote_veth, config[i].namespace);
|
||||
SYS(fail, "ip link set dev %s up", config[i].local_veth);
|
||||
if (config[i].remote_addr)
|
||||
SYS(fail, "ip -n %s addr add %s/24 dev %s", config[i].namespace,
|
||||
config[i].remote_addr, config[i].remote_veth);
|
||||
SYS(fail, "ip -n %s link set dev %s up", config[i].namespace,
|
||||
config[i].remote_veth);
|
||||
}
|
||||
memcpy(net_config, &default_config, sizeof(struct net_configuration));
|
||||
|
||||
/* Then configure the redirect map and attach programs to interfaces */
|
||||
map_fd = bpf_map__fd(skeletons->xdp_redirect_maps->maps.tx_port);
|
||||
if (!ASSERT_GE(map_fd, 0, "open redirect map"))
|
||||
/* Create unique namespaces */
|
||||
err = append_tid(net_config->ns0_name, NS_NAME_MAX_LEN);
|
||||
if (!ASSERT_OK(err, "append TID to ns0 name"))
|
||||
goto fail;
|
||||
SYS(fail, "ip netns add %s", net_config->ns0_name);
|
||||
|
||||
for (i = 0; i < VETH_PAIRS_COUNT; i++) {
|
||||
interface_id = if_nametoindex(config[i].next_veth);
|
||||
if (!ASSERT_NEQ(interface_id, 0, "non zero interface index"))
|
||||
goto fail;
|
||||
err = bpf_map_update_elem(map_fd, &i, &interface_id, BPF_ANY);
|
||||
if (!ASSERT_OK(err, "configure interface redirection through map"))
|
||||
goto fail;
|
||||
if (attach_programs_to_veth_pair(skeletons, i))
|
||||
err = append_tid(net_config->veth_cfg[i].namespace, NS_NAME_MAX_LEN);
|
||||
if (!ASSERT_OK(err, "append TID to ns name"))
|
||||
goto fail;
|
||||
SYS(fail, "ip netns add %s", net_config->veth_cfg[i].namespace);
|
||||
}
|
||||
|
||||
/* Create interfaces */
|
||||
nstoken = open_netns(net_config->ns0_name);
|
||||
if (!nstoken)
|
||||
goto fail;
|
||||
|
||||
for (i = 0; i < VETH_PAIRS_COUNT; i++) {
|
||||
SYS(fail, "ip link add %s type veth peer name %s netns %s",
|
||||
net_config->veth_cfg[i].local_veth, net_config->veth_cfg[i].remote_veth,
|
||||
net_config->veth_cfg[i].namespace);
|
||||
SYS(fail, "ip link set dev %s up", net_config->veth_cfg[i].local_veth);
|
||||
if (net_config->veth_cfg[i].remote_addr[0])
|
||||
SYS(fail, "ip -n %s addr add %s/24 dev %s",
|
||||
net_config->veth_cfg[i].namespace,
|
||||
net_config->veth_cfg[i].remote_addr,
|
||||
net_config->veth_cfg[i].remote_veth);
|
||||
SYS(fail, "ip -n %s link set dev %s up", net_config->veth_cfg[i].namespace,
|
||||
net_config->veth_cfg[i].remote_veth);
|
||||
}
|
||||
|
||||
close_netns(nstoken);
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
close_netns(nstoken);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void cleanup_network(void)
|
||||
static void cleanup_network(struct net_configuration *net_config)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Deleting namespaces is enough to automatically remove veth pairs as well
|
||||
*/
|
||||
SYS_NOFAIL("ip netns del %s", net_config->ns0_name);
|
||||
for (i = 0; i < VETH_PAIRS_COUNT; i++)
|
||||
SYS_NOFAIL("ip netns del %s", config[i].namespace);
|
||||
SYS_NOFAIL("ip netns del %s", net_config->veth_cfg[i].namespace);
|
||||
}
|
||||
|
||||
static int check_ping(struct skeletons *skeletons)
|
||||
#define VETH_REDIRECT_SKEL_NB 3
|
||||
static void xdp_veth_redirect(u32 flags)
|
||||
{
|
||||
struct prog_configuration ping_config[VETH_PAIRS_COUNT] = {
|
||||
{
|
||||
.local_name = "xdp_redirect_map_0",
|
||||
.remote_name = "xdp_dummy_prog",
|
||||
.local_flags = flags,
|
||||
.remote_flags = flags,
|
||||
},
|
||||
{
|
||||
.local_name = "xdp_redirect_map_1",
|
||||
.remote_name = "xdp_tx",
|
||||
.local_flags = flags,
|
||||
.remote_flags = flags,
|
||||
},
|
||||
{
|
||||
.local_name = "xdp_redirect_map_2",
|
||||
.remote_name = "xdp_dummy_prog",
|
||||
.local_flags = flags,
|
||||
.remote_flags = flags,
|
||||
}
|
||||
};
|
||||
struct bpf_object *bpf_objs[VETH_REDIRECT_SKEL_NB];
|
||||
struct xdp_redirect_map *xdp_redirect_map;
|
||||
struct net_configuration net_config;
|
||||
struct nstoken *nstoken = NULL;
|
||||
struct xdp_dummy *xdp_dummy;
|
||||
struct xdp_tx *xdp_tx;
|
||||
int map_fd;
|
||||
int i;
|
||||
|
||||
xdp_dummy = xdp_dummy__open_and_load();
|
||||
if (!ASSERT_OK_PTR(xdp_dummy, "xdp_dummy__open_and_load"))
|
||||
return;
|
||||
|
||||
xdp_tx = xdp_tx__open_and_load();
|
||||
if (!ASSERT_OK_PTR(xdp_tx, "xdp_tx__open_and_load"))
|
||||
goto destroy_xdp_dummy;
|
||||
|
||||
xdp_redirect_map = xdp_redirect_map__open_and_load();
|
||||
if (!ASSERT_OK_PTR(xdp_redirect_map, "xdp_redirect_map__open_and_load"))
|
||||
goto destroy_xdp_tx;
|
||||
|
||||
if (!ASSERT_OK(create_network(&net_config), "create network"))
|
||||
goto destroy_xdp_redirect_map;
|
||||
|
||||
/* Then configure the redirect map and attach programs to interfaces */
|
||||
map_fd = bpf_map__fd(xdp_redirect_map->maps.tx_port);
|
||||
if (!ASSERT_OK_FD(map_fd, "open redirect map"))
|
||||
goto destroy_xdp_redirect_map;
|
||||
|
||||
bpf_objs[0] = xdp_dummy->obj;
|
||||
bpf_objs[1] = xdp_tx->obj;
|
||||
bpf_objs[2] = xdp_redirect_map->obj;
|
||||
|
||||
nstoken = open_netns(net_config.ns0_name);
|
||||
if (!ASSERT_OK_PTR(nstoken, "open NS0"))
|
||||
goto destroy_xdp_redirect_map;
|
||||
|
||||
for (i = 0; i < VETH_PAIRS_COUNT; i++) {
|
||||
int next_veth = net_config.veth_cfg[i].next_veth;
|
||||
int interface_id;
|
||||
int err;
|
||||
|
||||
interface_id = if_nametoindex(net_config.veth_cfg[next_veth].local_veth);
|
||||
if (!ASSERT_NEQ(interface_id, 0, "non zero interface index"))
|
||||
goto destroy_xdp_redirect_map;
|
||||
err = bpf_map_update_elem(map_fd, &i, &interface_id, BPF_ANY);
|
||||
if (!ASSERT_OK(err, "configure interface redirection through map"))
|
||||
goto destroy_xdp_redirect_map;
|
||||
if (attach_programs_to_veth_pair(bpf_objs, VETH_REDIRECT_SKEL_NB,
|
||||
&net_config, ping_config, i))
|
||||
goto destroy_xdp_redirect_map;
|
||||
}
|
||||
|
||||
/* Test: if all interfaces are properly configured, we must be able to ping
|
||||
* veth33 from veth11
|
||||
*/
|
||||
return SYS_NOFAIL("ip netns exec %s ping -c 1 -W 1 %s > /dev/null",
|
||||
config[0].namespace, IP_DST);
|
||||
ASSERT_OK(SYS_NOFAIL("ip netns exec %s ping -c 1 -W 1 %s > /dev/null",
|
||||
net_config.veth_cfg[0].namespace, IP_DST), "ping");
|
||||
|
||||
destroy_xdp_redirect_map:
|
||||
close_netns(nstoken);
|
||||
xdp_redirect_map__destroy(xdp_redirect_map);
|
||||
destroy_xdp_tx:
|
||||
xdp_tx__destroy(xdp_tx);
|
||||
destroy_xdp_dummy:
|
||||
xdp_dummy__destroy(xdp_dummy);
|
||||
|
||||
cleanup_network(&net_config);
|
||||
}
|
||||
|
||||
#define BROADCAST_REDIRECT_SKEL_NB 2
|
||||
static void xdp_veth_broadcast_redirect(u32 attach_flags, u64 redirect_flags)
|
||||
{
|
||||
struct prog_configuration prog_cfg[VETH_PAIRS_COUNT] = {
|
||||
{
|
||||
.local_name = "xdp_redirect_map_multi_prog",
|
||||
.remote_name = "xdp_count_0",
|
||||
.local_flags = attach_flags,
|
||||
.remote_flags = attach_flags,
|
||||
},
|
||||
{
|
||||
.local_name = "xdp_redirect_map_multi_prog",
|
||||
.remote_name = "xdp_count_1",
|
||||
.local_flags = attach_flags,
|
||||
.remote_flags = attach_flags,
|
||||
},
|
||||
{
|
||||
.local_name = "xdp_redirect_map_multi_prog",
|
||||
.remote_name = "xdp_count_2",
|
||||
.local_flags = attach_flags,
|
||||
.remote_flags = attach_flags,
|
||||
}
|
||||
};
|
||||
struct bpf_object *bpf_objs[BROADCAST_REDIRECT_SKEL_NB];
|
||||
struct xdp_redirect_multi_kern *xdp_redirect_multi_kern;
|
||||
struct xdp_redirect_map *xdp_redirect_map;
|
||||
struct bpf_devmap_val devmap_val = {};
|
||||
struct net_configuration net_config;
|
||||
struct nstoken *nstoken = NULL;
|
||||
u16 protocol = ETH_P_IP;
|
||||
int group_map;
|
||||
int flags_map;
|
||||
int cnt_map;
|
||||
u64 cnt = 0;
|
||||
int i, err;
|
||||
|
||||
xdp_redirect_multi_kern = xdp_redirect_multi_kern__open_and_load();
|
||||
if (!ASSERT_OK_PTR(xdp_redirect_multi_kern, "xdp_redirect_multi_kern__open_and_load"))
|
||||
return;
|
||||
|
||||
xdp_redirect_map = xdp_redirect_map__open_and_load();
|
||||
if (!ASSERT_OK_PTR(xdp_redirect_map, "xdp_redirect_map__open_and_load"))
|
||||
goto destroy_xdp_redirect_multi_kern;
|
||||
|
||||
if (!ASSERT_OK(create_network(&net_config), "create network"))
|
||||
goto destroy_xdp_redirect_map;
|
||||
|
||||
group_map = bpf_map__fd(xdp_redirect_multi_kern->maps.map_all);
|
||||
if (!ASSERT_OK_FD(group_map, "open map_all"))
|
||||
goto destroy_xdp_redirect_map;
|
||||
|
||||
flags_map = bpf_map__fd(xdp_redirect_multi_kern->maps.redirect_flags);
|
||||
if (!ASSERT_OK_FD(group_map, "open map_all"))
|
||||
goto destroy_xdp_redirect_map;
|
||||
|
||||
err = bpf_map_update_elem(flags_map, &protocol, &redirect_flags, BPF_NOEXIST);
|
||||
if (!ASSERT_OK(err, "init IP count"))
|
||||
goto destroy_xdp_redirect_map;
|
||||
|
||||
cnt_map = bpf_map__fd(xdp_redirect_map->maps.rxcnt);
|
||||
if (!ASSERT_OK_FD(cnt_map, "open rxcnt map"))
|
||||
goto destroy_xdp_redirect_map;
|
||||
|
||||
bpf_objs[0] = xdp_redirect_multi_kern->obj;
|
||||
bpf_objs[1] = xdp_redirect_map->obj;
|
||||
|
||||
nstoken = open_netns(net_config.ns0_name);
|
||||
if (!ASSERT_OK_PTR(nstoken, "open NS0"))
|
||||
goto destroy_xdp_redirect_map;
|
||||
|
||||
for (i = 0; i < VETH_PAIRS_COUNT; i++) {
|
||||
int ifindex = if_nametoindex(net_config.veth_cfg[i].local_veth);
|
||||
|
||||
if (attach_programs_to_veth_pair(bpf_objs, BROADCAST_REDIRECT_SKEL_NB,
|
||||
&net_config, prog_cfg, i))
|
||||
goto destroy_xdp_redirect_map;
|
||||
|
||||
SYS(destroy_xdp_redirect_map,
|
||||
"ip -n %s neigh add %s lladdr 00:00:00:00:00:01 dev %s",
|
||||
net_config.veth_cfg[i].namespace, IP_NEIGH, net_config.veth_cfg[i].remote_veth);
|
||||
|
||||
devmap_val.ifindex = ifindex;
|
||||
err = bpf_map_update_elem(group_map, &ifindex, &devmap_val, 0);
|
||||
if (!ASSERT_OK(err, "bpf_map_update_elem"))
|
||||
goto destroy_xdp_redirect_map;
|
||||
|
||||
}
|
||||
|
||||
SYS_NOFAIL("ip netns exec %s ping %s -i 0.1 -c 4 -W1 > /dev/null ",
|
||||
net_config.veth_cfg[0].namespace, IP_NEIGH);
|
||||
|
||||
for (i = 0; i < VETH_PAIRS_COUNT; i++) {
|
||||
err = bpf_map_lookup_elem(cnt_map, &i, &cnt);
|
||||
if (!ASSERT_OK(err, "get IP cnt"))
|
||||
goto destroy_xdp_redirect_map;
|
||||
|
||||
if (redirect_flags & BPF_F_EXCLUDE_INGRESS)
|
||||
/* veth11 shouldn't receive the ICMP requests;
|
||||
* others should
|
||||
*/
|
||||
ASSERT_EQ(cnt, i ? 4 : 0, "compare IP cnt");
|
||||
else
|
||||
/* All remote veth should receive the ICMP requests */
|
||||
ASSERT_EQ(cnt, 4, "compare IP cnt");
|
||||
}
|
||||
|
||||
destroy_xdp_redirect_map:
|
||||
close_netns(nstoken);
|
||||
xdp_redirect_map__destroy(xdp_redirect_map);
|
||||
destroy_xdp_redirect_multi_kern:
|
||||
xdp_redirect_multi_kern__destroy(xdp_redirect_multi_kern);
|
||||
|
||||
cleanup_network(&net_config);
|
||||
}
|
||||
|
||||
#define VETH_EGRESS_SKEL_NB 3
|
||||
static void xdp_veth_egress(u32 flags)
|
||||
{
|
||||
struct prog_configuration prog_cfg[VETH_PAIRS_COUNT] = {
|
||||
{
|
||||
.local_name = "xdp_redirect_map_all_prog",
|
||||
.remote_name = "xdp_dummy_prog",
|
||||
.local_flags = flags,
|
||||
.remote_flags = flags,
|
||||
},
|
||||
{
|
||||
.local_name = "xdp_redirect_map_all_prog",
|
||||
.remote_name = "store_mac_1",
|
||||
.local_flags = flags,
|
||||
.remote_flags = flags,
|
||||
},
|
||||
{
|
||||
.local_name = "xdp_redirect_map_all_prog",
|
||||
.remote_name = "store_mac_2",
|
||||
.local_flags = flags,
|
||||
.remote_flags = flags,
|
||||
}
|
||||
};
|
||||
const char magic_mac[6] = { 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF};
|
||||
struct xdp_redirect_multi_kern *xdp_redirect_multi_kern;
|
||||
struct bpf_object *bpf_objs[VETH_EGRESS_SKEL_NB];
|
||||
struct xdp_redirect_map *xdp_redirect_map;
|
||||
struct bpf_devmap_val devmap_val = {};
|
||||
struct net_configuration net_config;
|
||||
int mac_map, egress_map, res_map;
|
||||
struct nstoken *nstoken = NULL;
|
||||
struct xdp_dummy *xdp_dummy;
|
||||
int err;
|
||||
int i;
|
||||
|
||||
xdp_dummy = xdp_dummy__open_and_load();
|
||||
if (!ASSERT_OK_PTR(xdp_dummy, "xdp_dummy__open_and_load"))
|
||||
return;
|
||||
|
||||
xdp_redirect_multi_kern = xdp_redirect_multi_kern__open_and_load();
|
||||
if (!ASSERT_OK_PTR(xdp_redirect_multi_kern, "xdp_redirect_multi_kern__open_and_load"))
|
||||
goto destroy_xdp_dummy;
|
||||
|
||||
xdp_redirect_map = xdp_redirect_map__open_and_load();
|
||||
if (!ASSERT_OK_PTR(xdp_redirect_map, "xdp_redirect_map__open_and_load"))
|
||||
goto destroy_xdp_redirect_multi_kern;
|
||||
|
||||
if (!ASSERT_OK(create_network(&net_config), "create network"))
|
||||
goto destroy_xdp_redirect_map;
|
||||
|
||||
mac_map = bpf_map__fd(xdp_redirect_multi_kern->maps.mac_map);
|
||||
if (!ASSERT_OK_FD(mac_map, "open mac_map"))
|
||||
goto destroy_xdp_redirect_map;
|
||||
|
||||
egress_map = bpf_map__fd(xdp_redirect_multi_kern->maps.map_egress);
|
||||
if (!ASSERT_OK_FD(egress_map, "open map_egress"))
|
||||
goto destroy_xdp_redirect_map;
|
||||
|
||||
devmap_val.bpf_prog.fd = bpf_program__fd(xdp_redirect_multi_kern->progs.xdp_devmap_prog);
|
||||
|
||||
bpf_objs[0] = xdp_dummy->obj;
|
||||
bpf_objs[1] = xdp_redirect_multi_kern->obj;
|
||||
bpf_objs[2] = xdp_redirect_map->obj;
|
||||
|
||||
nstoken = open_netns(net_config.ns0_name);
|
||||
if (!ASSERT_OK_PTR(nstoken, "open NS0"))
|
||||
goto destroy_xdp_redirect_map;
|
||||
|
||||
for (i = 0; i < VETH_PAIRS_COUNT; i++) {
|
||||
int ifindex = if_nametoindex(net_config.veth_cfg[i].local_veth);
|
||||
|
||||
SYS(destroy_xdp_redirect_map,
|
||||
"ip -n %s neigh add %s lladdr 00:00:00:00:00:01 dev %s",
|
||||
net_config.veth_cfg[i].namespace, IP_NEIGH, net_config.veth_cfg[i].remote_veth);
|
||||
|
||||
if (attach_programs_to_veth_pair(bpf_objs, VETH_REDIRECT_SKEL_NB,
|
||||
&net_config, prog_cfg, i))
|
||||
goto destroy_xdp_redirect_map;
|
||||
|
||||
err = bpf_map_update_elem(mac_map, &ifindex, magic_mac, 0);
|
||||
if (!ASSERT_OK(err, "bpf_map_update_elem"))
|
||||
goto destroy_xdp_redirect_map;
|
||||
|
||||
devmap_val.ifindex = ifindex;
|
||||
err = bpf_map_update_elem(egress_map, &ifindex, &devmap_val, 0);
|
||||
if (!ASSERT_OK(err, "bpf_map_update_elem"))
|
||||
goto destroy_xdp_redirect_map;
|
||||
}
|
||||
|
||||
SYS_NOFAIL("ip netns exec %s ping %s -i 0.1 -c 4 -W1 > /dev/null ",
|
||||
net_config.veth_cfg[0].namespace, IP_NEIGH);
|
||||
|
||||
res_map = bpf_map__fd(xdp_redirect_map->maps.rx_mac);
|
||||
if (!ASSERT_OK_FD(res_map, "open rx_map"))
|
||||
goto destroy_xdp_redirect_map;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
u32 key = i;
|
||||
u64 res;
|
||||
|
||||
err = bpf_map_lookup_elem(res_map, &key, &res);
|
||||
if (!ASSERT_OK(err, "get MAC res"))
|
||||
goto destroy_xdp_redirect_map;
|
||||
|
||||
ASSERT_STRNEQ((const char *)&res, magic_mac, ETH_ALEN, "compare mac");
|
||||
}
|
||||
|
||||
destroy_xdp_redirect_map:
|
||||
close_netns(nstoken);
|
||||
xdp_redirect_map__destroy(xdp_redirect_map);
|
||||
destroy_xdp_redirect_multi_kern:
|
||||
xdp_redirect_multi_kern__destroy(xdp_redirect_multi_kern);
|
||||
destroy_xdp_dummy:
|
||||
xdp_dummy__destroy(xdp_dummy);
|
||||
|
||||
cleanup_network(&net_config);
|
||||
}
|
||||
|
||||
void test_xdp_veth_redirect(void)
|
||||
{
|
||||
struct skeletons skeletons = {};
|
||||
if (test__start_subtest("0"))
|
||||
xdp_veth_redirect(0);
|
||||
|
||||
skeletons.xdp_dummy = xdp_dummy__open_and_load();
|
||||
if (!ASSERT_OK_PTR(skeletons.xdp_dummy, "xdp_dummy__open_and_load"))
|
||||
return;
|
||||
if (test__start_subtest("DRV_MODE"))
|
||||
xdp_veth_redirect(XDP_FLAGS_DRV_MODE);
|
||||
|
||||
skeletons.xdp_tx = xdp_tx__open_and_load();
|
||||
if (!ASSERT_OK_PTR(skeletons.xdp_tx, "xdp_tx__open_and_load"))
|
||||
goto destroy_xdp_dummy;
|
||||
|
||||
skeletons.xdp_redirect_maps = xdp_redirect_map__open_and_load();
|
||||
if (!ASSERT_OK_PTR(skeletons.xdp_redirect_maps, "xdp_redirect_map__open_and_load"))
|
||||
goto destroy_xdp_tx;
|
||||
|
||||
if (configure_network(&skeletons))
|
||||
goto destroy_xdp_redirect_map;
|
||||
|
||||
ASSERT_OK(check_ping(&skeletons), "ping");
|
||||
|
||||
destroy_xdp_redirect_map:
|
||||
xdp_redirect_map__destroy(skeletons.xdp_redirect_maps);
|
||||
destroy_xdp_tx:
|
||||
xdp_tx__destroy(skeletons.xdp_tx);
|
||||
destroy_xdp_dummy:
|
||||
xdp_dummy__destroy(skeletons.xdp_dummy);
|
||||
|
||||
cleanup_network();
|
||||
if (test__start_subtest("SKB_MODE"))
|
||||
xdp_veth_redirect(XDP_FLAGS_SKB_MODE);
|
||||
}
|
||||
|
||||
void test_xdp_veth_broadcast_redirect(void)
|
||||
{
|
||||
if (test__start_subtest("0/BROADCAST"))
|
||||
xdp_veth_broadcast_redirect(0, BPF_F_BROADCAST);
|
||||
|
||||
if (test__start_subtest("0/(BROADCAST | EXCLUDE_INGRESS)"))
|
||||
xdp_veth_broadcast_redirect(0, BPF_F_BROADCAST | BPF_F_EXCLUDE_INGRESS);
|
||||
|
||||
if (test__start_subtest("DRV_MODE/BROADCAST"))
|
||||
xdp_veth_broadcast_redirect(XDP_FLAGS_DRV_MODE, BPF_F_BROADCAST);
|
||||
|
||||
if (test__start_subtest("DRV_MODE/(BROADCAST | EXCLUDE_INGRESS)"))
|
||||
xdp_veth_broadcast_redirect(XDP_FLAGS_DRV_MODE,
|
||||
BPF_F_BROADCAST | BPF_F_EXCLUDE_INGRESS);
|
||||
|
||||
if (test__start_subtest("SKB_MODE/BROADCAST"))
|
||||
xdp_veth_broadcast_redirect(XDP_FLAGS_SKB_MODE, BPF_F_BROADCAST);
|
||||
|
||||
if (test__start_subtest("SKB_MODE/(BROADCAST | EXCLUDE_INGRESS)"))
|
||||
xdp_veth_broadcast_redirect(XDP_FLAGS_SKB_MODE,
|
||||
BPF_F_BROADCAST | BPF_F_EXCLUDE_INGRESS);
|
||||
}
|
||||
|
||||
void test_xdp_veth_egress(void)
|
||||
{
|
||||
if (test__start_subtest("0/egress"))
|
||||
xdp_veth_egress(0);
|
||||
|
||||
if (test__start_subtest("DRV_MODE/egress"))
|
||||
xdp_veth_egress(XDP_FLAGS_DRV_MODE);
|
||||
|
||||
if (test__start_subtest("SKB_MODE/egress"))
|
||||
xdp_veth_egress(XDP_FLAGS_SKB_MODE);
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "priv_prog.skel.h"
|
||||
#include "dummy_st_ops_success.skel.h"
|
||||
#include "token_lsm.skel.h"
|
||||
#include "priv_freplace_prog.skel.h"
|
||||
|
||||
static inline int sys_mount(const char *dev_name, const char *dir_name,
|
||||
const char *type, unsigned long flags,
|
||||
@@ -788,6 +789,84 @@ static int userns_obj_priv_prog(int mnt_fd, struct token_lsm *lsm_skel)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int userns_obj_priv_freplace_setup(int mnt_fd, struct priv_freplace_prog **fr_skel,
|
||||
struct priv_prog **skel, int *tgt_fd)
|
||||
{
|
||||
LIBBPF_OPTS(bpf_object_open_opts, opts);
|
||||
int err;
|
||||
char buf[256];
|
||||
|
||||
/* use bpf_token_path to provide BPF FS path */
|
||||
snprintf(buf, sizeof(buf), "/proc/self/fd/%d", mnt_fd);
|
||||
opts.bpf_token_path = buf;
|
||||
*skel = priv_prog__open_opts(&opts);
|
||||
if (!ASSERT_OK_PTR(*skel, "priv_prog__open_opts"))
|
||||
return -EINVAL;
|
||||
err = priv_prog__load(*skel);
|
||||
if (!ASSERT_OK(err, "priv_prog__load"))
|
||||
return -EINVAL;
|
||||
|
||||
*fr_skel = priv_freplace_prog__open_opts(&opts);
|
||||
if (!ASSERT_OK_PTR(*skel, "priv_freplace_prog__open_opts"))
|
||||
return -EINVAL;
|
||||
|
||||
*tgt_fd = bpf_program__fd((*skel)->progs.xdp_prog1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Verify that freplace works from user namespace, because bpf token is loaded
|
||||
* in bpf_object__prepare
|
||||
*/
|
||||
static int userns_obj_priv_freplace_prog(int mnt_fd, struct token_lsm *lsm_skel)
|
||||
{
|
||||
struct priv_freplace_prog *fr_skel = NULL;
|
||||
struct priv_prog *skel = NULL;
|
||||
int err, tgt_fd;
|
||||
|
||||
err = userns_obj_priv_freplace_setup(mnt_fd, &fr_skel, &skel, &tgt_fd);
|
||||
if (!ASSERT_OK(err, "setup"))
|
||||
goto out;
|
||||
|
||||
err = bpf_object__prepare(fr_skel->obj);
|
||||
if (!ASSERT_OK(err, "freplace__prepare"))
|
||||
goto out;
|
||||
|
||||
err = bpf_program__set_attach_target(fr_skel->progs.new_xdp_prog2, tgt_fd, "xdp_prog1");
|
||||
if (!ASSERT_OK(err, "set_attach_target"))
|
||||
goto out;
|
||||
|
||||
err = priv_freplace_prog__load(fr_skel);
|
||||
ASSERT_OK(err, "priv_freplace_prog__load");
|
||||
|
||||
out:
|
||||
priv_freplace_prog__destroy(fr_skel);
|
||||
priv_prog__destroy(skel);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Verify that replace fails to set attach target from user namespace without bpf token */
|
||||
static int userns_obj_priv_freplace_prog_fail(int mnt_fd, struct token_lsm *lsm_skel)
|
||||
{
|
||||
struct priv_freplace_prog *fr_skel = NULL;
|
||||
struct priv_prog *skel = NULL;
|
||||
int err, tgt_fd;
|
||||
|
||||
err = userns_obj_priv_freplace_setup(mnt_fd, &fr_skel, &skel, &tgt_fd);
|
||||
if (!ASSERT_OK(err, "setup"))
|
||||
goto out;
|
||||
|
||||
err = bpf_program__set_attach_target(fr_skel->progs.new_xdp_prog2, tgt_fd, "xdp_prog1");
|
||||
if (ASSERT_ERR(err, "attach fails"))
|
||||
err = 0;
|
||||
else
|
||||
err = -EINVAL;
|
||||
|
||||
out:
|
||||
priv_freplace_prog__destroy(fr_skel);
|
||||
priv_prog__destroy(skel);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* this test is called with BPF FS that doesn't delegate BPF_BTF_LOAD command,
|
||||
* which should cause struct_ops application to fail, as BTF won't be uploaded
|
||||
* into the kernel, even if STRUCT_OPS programs themselves are allowed
|
||||
@@ -1004,12 +1083,28 @@ void test_token(void)
|
||||
if (test__start_subtest("obj_priv_prog")) {
|
||||
struct bpffs_opts opts = {
|
||||
.cmds = bit(BPF_PROG_LOAD),
|
||||
.progs = bit(BPF_PROG_TYPE_KPROBE),
|
||||
.progs = bit(BPF_PROG_TYPE_XDP),
|
||||
.attachs = ~0ULL,
|
||||
};
|
||||
|
||||
subtest_userns(&opts, userns_obj_priv_prog);
|
||||
}
|
||||
if (test__start_subtest("obj_priv_freplace_prog")) {
|
||||
struct bpffs_opts opts = {
|
||||
.cmds = bit(BPF_BTF_LOAD) | bit(BPF_PROG_LOAD) | bit(BPF_BTF_GET_FD_BY_ID),
|
||||
.progs = bit(BPF_PROG_TYPE_EXT) | bit(BPF_PROG_TYPE_XDP),
|
||||
.attachs = ~0ULL,
|
||||
};
|
||||
subtest_userns(&opts, userns_obj_priv_freplace_prog);
|
||||
}
|
||||
if (test__start_subtest("obj_priv_freplace_prog_fail")) {
|
||||
struct bpffs_opts opts = {
|
||||
.cmds = bit(BPF_BTF_LOAD) | bit(BPF_PROG_LOAD) | bit(BPF_BTF_GET_FD_BY_ID),
|
||||
.progs = bit(BPF_PROG_TYPE_EXT) | bit(BPF_PROG_TYPE_XDP),
|
||||
.attachs = ~0ULL,
|
||||
};
|
||||
subtest_userns(&opts, userns_obj_priv_freplace_prog_fail);
|
||||
}
|
||||
if (test__start_subtest("obj_priv_btf_fail")) {
|
||||
struct bpffs_opts opts = {
|
||||
/* disallow BTF loading */
|
||||
|
||||
@@ -45,7 +45,7 @@ static void subtest_basic_usdt(void)
|
||||
LIBBPF_OPTS(bpf_usdt_opts, opts);
|
||||
struct test_usdt *skel;
|
||||
struct test_usdt__bss *bss;
|
||||
int err;
|
||||
int err, i;
|
||||
|
||||
skel = test_usdt__open_and_load();
|
||||
if (!ASSERT_OK_PTR(skel, "skel_open"))
|
||||
@@ -75,6 +75,7 @@ static void subtest_basic_usdt(void)
|
||||
ASSERT_EQ(bss->usdt0_cookie, 0xcafedeadbeeffeed, "usdt0_cookie");
|
||||
ASSERT_EQ(bss->usdt0_arg_cnt, 0, "usdt0_arg_cnt");
|
||||
ASSERT_EQ(bss->usdt0_arg_ret, -ENOENT, "usdt0_arg_ret");
|
||||
ASSERT_EQ(bss->usdt0_arg_size, -ENOENT, "usdt0_arg_size");
|
||||
|
||||
/* auto-attached usdt3 gets default zero cookie value */
|
||||
ASSERT_EQ(bss->usdt3_cookie, 0, "usdt3_cookie");
|
||||
@@ -86,6 +87,9 @@ static void subtest_basic_usdt(void)
|
||||
ASSERT_EQ(bss->usdt3_args[0], 1, "usdt3_arg1");
|
||||
ASSERT_EQ(bss->usdt3_args[1], 42, "usdt3_arg2");
|
||||
ASSERT_EQ(bss->usdt3_args[2], (uintptr_t)&bla, "usdt3_arg3");
|
||||
ASSERT_EQ(bss->usdt3_arg_sizes[0], 4, "usdt3_arg1_size");
|
||||
ASSERT_EQ(bss->usdt3_arg_sizes[1], 8, "usdt3_arg2_size");
|
||||
ASSERT_EQ(bss->usdt3_arg_sizes[2], 8, "usdt3_arg3_size");
|
||||
|
||||
/* auto-attached usdt12 gets default zero cookie value */
|
||||
ASSERT_EQ(bss->usdt12_cookie, 0, "usdt12_cookie");
|
||||
@@ -104,6 +108,11 @@ static void subtest_basic_usdt(void)
|
||||
ASSERT_EQ(bss->usdt12_args[10], nums[idx], "usdt12_arg11");
|
||||
ASSERT_EQ(bss->usdt12_args[11], t1.y, "usdt12_arg12");
|
||||
|
||||
int usdt12_expected_arg_sizes[12] = { 4, 4, 8, 8, 4, 8, 8, 8, 4, 2, 2, 1 };
|
||||
|
||||
for (i = 0; i < 12; i++)
|
||||
ASSERT_EQ(bss->usdt12_arg_sizes[i], usdt12_expected_arg_sizes[i], "usdt12_arg_size");
|
||||
|
||||
/* trigger_func() is marked __always_inline, so USDT invocations will be
|
||||
* inlined in two different places, meaning that each USDT will have
|
||||
* at least 2 different places to be attached to. This verifies that
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
#include "verifier_ldsx.skel.h"
|
||||
#include "verifier_leak_ptr.skel.h"
|
||||
#include "verifier_linked_scalars.skel.h"
|
||||
#include "verifier_load_acquire.skel.h"
|
||||
#include "verifier_loops1.skel.h"
|
||||
#include "verifier_lwt.skel.h"
|
||||
#include "verifier_map_in_map.skel.h"
|
||||
@@ -80,6 +81,7 @@
|
||||
#include "verifier_spill_fill.skel.h"
|
||||
#include "verifier_spin_lock.skel.h"
|
||||
#include "verifier_stack_ptr.skel.h"
|
||||
#include "verifier_store_release.skel.h"
|
||||
#include "verifier_subprog_precision.skel.h"
|
||||
#include "verifier_subreg.skel.h"
|
||||
#include "verifier_tailcall_jit.skel.h"
|
||||
@@ -121,7 +123,7 @@ static void run_tests_aux(const char *skel_name,
|
||||
/* test_verifier tests are executed w/o CAP_SYS_ADMIN, do the same here */
|
||||
err = cap_disable_effective(1ULL << CAP_SYS_ADMIN, &old_caps);
|
||||
if (err) {
|
||||
PRINT_FAIL("failed to drop CAP_SYS_ADMIN: %i, %s\n", err, strerror(err));
|
||||
PRINT_FAIL("failed to drop CAP_SYS_ADMIN: %i, %s\n", err, strerror(-err));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -131,7 +133,7 @@ static void run_tests_aux(const char *skel_name,
|
||||
|
||||
err = cap_enable_effective(old_caps, NULL);
|
||||
if (err)
|
||||
PRINT_FAIL("failed to restore CAP_SYS_ADMIN: %i, %s\n", err, strerror(err));
|
||||
PRINT_FAIL("failed to restore CAP_SYS_ADMIN: %i, %s\n", err, strerror(-err));
|
||||
}
|
||||
|
||||
#define RUN(skel) run_tests_aux(#skel, skel##__elf_bytes, NULL)
|
||||
@@ -173,6 +175,7 @@ void test_verifier_int_ptr(void) { RUN(verifier_int_ptr); }
|
||||
void test_verifier_iterating_callbacks(void) { RUN(verifier_iterating_callbacks); }
|
||||
void test_verifier_jeq_infer_not_null(void) { RUN(verifier_jeq_infer_not_null); }
|
||||
void test_verifier_jit_convergence(void) { RUN(verifier_jit_convergence); }
|
||||
void test_verifier_load_acquire(void) { RUN(verifier_load_acquire); }
|
||||
void test_verifier_ld_ind(void) { RUN(verifier_ld_ind); }
|
||||
void test_verifier_ldsx(void) { RUN(verifier_ldsx); }
|
||||
void test_verifier_leak_ptr(void) { RUN(verifier_leak_ptr); }
|
||||
@@ -211,6 +214,7 @@ void test_verifier_sockmap_mutate(void) { RUN(verifier_sockmap_mutate); }
|
||||
void test_verifier_spill_fill(void) { RUN(verifier_spill_fill); }
|
||||
void test_verifier_spin_lock(void) { RUN(verifier_spin_lock); }
|
||||
void test_verifier_stack_ptr(void) { RUN(verifier_stack_ptr); }
|
||||
void test_verifier_store_release(void) { RUN(verifier_store_release); }
|
||||
void test_verifier_subprog_precision(void) { RUN(verifier_subprog_precision); }
|
||||
void test_verifier_subreg(void) { RUN(verifier_subreg); }
|
||||
void test_verifier_tailcall_jit(void) { RUN(verifier_tailcall_jit); }
|
||||
|
||||
175
tools/testing/selftests/bpf/prog_tests/xdp_vlan.c
Normal file
175
tools/testing/selftests/bpf/prog_tests/xdp_vlan.c
Normal file
@@ -0,0 +1,175 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
/*
|
||||
* Network topology:
|
||||
* ----------- -----------
|
||||
* | NS1 | | NS2 |
|
||||
* | veth0 -|--------|- veth0 |
|
||||
* ----------- -----------
|
||||
*
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <net/if.h>
|
||||
#include <uapi/linux/if_link.h>
|
||||
|
||||
#include "network_helpers.h"
|
||||
#include "test_progs.h"
|
||||
#include "test_xdp_vlan.skel.h"
|
||||
|
||||
|
||||
#define VETH_NAME "veth0"
|
||||
#define NS_MAX_SIZE 32
|
||||
#define NS1_NAME "ns-xdp-vlan-1-"
|
||||
#define NS2_NAME "ns-xdp-vlan-2-"
|
||||
#define NS1_IP_ADDR "100.64.10.1"
|
||||
#define NS2_IP_ADDR "100.64.10.2"
|
||||
#define VLAN_ID 4011
|
||||
|
||||
static int setup_network(char *ns1, char *ns2)
|
||||
{
|
||||
if (!ASSERT_OK(append_tid(ns1, NS_MAX_SIZE), "create ns1 name"))
|
||||
goto fail;
|
||||
if (!ASSERT_OK(append_tid(ns2, NS_MAX_SIZE), "create ns2 name"))
|
||||
goto fail;
|
||||
|
||||
SYS(fail, "ip netns add %s", ns1);
|
||||
SYS(fail, "ip netns add %s", ns2);
|
||||
SYS(fail, "ip -n %s link add %s type veth peer name %s netns %s",
|
||||
ns1, VETH_NAME, VETH_NAME, ns2);
|
||||
|
||||
/* NOTICE: XDP require VLAN header inside packet payload
|
||||
* - Thus, disable VLAN offloading driver features
|
||||
*/
|
||||
SYS(fail, "ip netns exec %s ethtool -K %s rxvlan off txvlan off", ns1, VETH_NAME);
|
||||
SYS(fail, "ip netns exec %s ethtool -K %s rxvlan off txvlan off", ns2, VETH_NAME);
|
||||
|
||||
/* NS1 configuration */
|
||||
SYS(fail, "ip -n %s addr add %s/24 dev %s", ns1, NS1_IP_ADDR, VETH_NAME);
|
||||
SYS(fail, "ip -n %s link set %s up", ns1, VETH_NAME);
|
||||
|
||||
/* NS2 configuration */
|
||||
SYS(fail, "ip -n %s link add link %s name %s.%d type vlan id %d",
|
||||
ns2, VETH_NAME, VETH_NAME, VLAN_ID, VLAN_ID);
|
||||
SYS(fail, "ip -n %s addr add %s/24 dev %s.%d", ns2, NS2_IP_ADDR, VETH_NAME, VLAN_ID);
|
||||
SYS(fail, "ip -n %s link set %s up", ns2, VETH_NAME);
|
||||
SYS(fail, "ip -n %s link set %s.%d up", ns2, VETH_NAME, VLAN_ID);
|
||||
|
||||
/* At this point ping should fail because VLAN tags are only used by NS2 */
|
||||
return !SYS_NOFAIL("ip netns exec %s ping -W 1 -c1 %s", ns2, NS1_IP_ADDR);
|
||||
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void cleanup_network(const char *ns1, const char *ns2)
|
||||
{
|
||||
SYS_NOFAIL("ip netns del %s", ns1);
|
||||
SYS_NOFAIL("ip netns del %s", ns2);
|
||||
}
|
||||
|
||||
static void xdp_vlan(struct bpf_program *xdp, struct bpf_program *tc, u32 flags)
|
||||
{
|
||||
LIBBPF_OPTS(bpf_tc_hook, tc_hook, .attach_point = BPF_TC_EGRESS);
|
||||
LIBBPF_OPTS(bpf_tc_opts, tc_opts, .handle = 1, .priority = 1);
|
||||
char ns1[NS_MAX_SIZE] = NS1_NAME;
|
||||
char ns2[NS_MAX_SIZE] = NS2_NAME;
|
||||
struct nstoken *nstoken = NULL;
|
||||
int interface;
|
||||
int ret;
|
||||
|
||||
if (!ASSERT_OK(setup_network(ns1, ns2), "setup network"))
|
||||
goto cleanup;
|
||||
|
||||
nstoken = open_netns(ns1);
|
||||
if (!ASSERT_OK_PTR(nstoken, "open NS1"))
|
||||
goto cleanup;
|
||||
|
||||
interface = if_nametoindex(VETH_NAME);
|
||||
if (!ASSERT_NEQ(interface, 0, "get interface index"))
|
||||
goto cleanup;
|
||||
|
||||
ret = bpf_xdp_attach(interface, bpf_program__fd(xdp), flags, NULL);
|
||||
if (!ASSERT_OK(ret, "attach xdp_vlan_change"))
|
||||
goto cleanup;
|
||||
|
||||
tc_hook.ifindex = interface;
|
||||
ret = bpf_tc_hook_create(&tc_hook);
|
||||
if (!ASSERT_OK(ret, "bpf_tc_hook_create"))
|
||||
goto detach_xdp;
|
||||
|
||||
/* Now we'll use BPF programs to pop/push the VLAN tags */
|
||||
tc_opts.prog_fd = bpf_program__fd(tc);
|
||||
ret = bpf_tc_attach(&tc_hook, &tc_opts);
|
||||
if (!ASSERT_OK(ret, "bpf_tc_attach"))
|
||||
goto detach_xdp;
|
||||
|
||||
close_netns(nstoken);
|
||||
nstoken = NULL;
|
||||
|
||||
/* Now the namespaces can reach each-other, test with pings */
|
||||
SYS(detach_tc, "ip netns exec %s ping -i 0.2 -W 2 -c 2 %s > /dev/null", ns1, NS2_IP_ADDR);
|
||||
SYS(detach_tc, "ip netns exec %s ping -i 0.2 -W 2 -c 2 %s > /dev/null", ns2, NS1_IP_ADDR);
|
||||
|
||||
|
||||
detach_tc:
|
||||
bpf_tc_detach(&tc_hook, &tc_opts);
|
||||
detach_xdp:
|
||||
bpf_xdp_detach(interface, flags, NULL);
|
||||
cleanup:
|
||||
close_netns(nstoken);
|
||||
cleanup_network(ns1, ns2);
|
||||
}
|
||||
|
||||
/* First test: Remove VLAN by setting VLAN ID 0, using "xdp_vlan_change"
|
||||
* egress use TC to add back VLAN tag 4011
|
||||
*/
|
||||
void test_xdp_vlan_change(void)
|
||||
{
|
||||
struct test_xdp_vlan *skel;
|
||||
|
||||
skel = test_xdp_vlan__open_and_load();
|
||||
if (!ASSERT_OK_PTR(skel, "xdp_vlan__open_and_load"))
|
||||
return;
|
||||
|
||||
if (test__start_subtest("0"))
|
||||
xdp_vlan(skel->progs.xdp_vlan_change, skel->progs.tc_vlan_push, 0);
|
||||
|
||||
if (test__start_subtest("DRV_MODE"))
|
||||
xdp_vlan(skel->progs.xdp_vlan_change, skel->progs.tc_vlan_push,
|
||||
XDP_FLAGS_DRV_MODE);
|
||||
|
||||
if (test__start_subtest("SKB_MODE"))
|
||||
xdp_vlan(skel->progs.xdp_vlan_change, skel->progs.tc_vlan_push,
|
||||
XDP_FLAGS_SKB_MODE);
|
||||
|
||||
test_xdp_vlan__destroy(skel);
|
||||
}
|
||||
|
||||
/* Second test: XDP prog fully remove vlan header
|
||||
*
|
||||
* Catch kernel bug for generic-XDP, that doesn't allow us to
|
||||
* remove a VLAN header, because skb->protocol still contain VLAN
|
||||
* ETH_P_8021Q indication, and this cause overwriting of our changes.
|
||||
*/
|
||||
void test_xdp_vlan_remove(void)
|
||||
{
|
||||
struct test_xdp_vlan *skel;
|
||||
|
||||
skel = test_xdp_vlan__open_and_load();
|
||||
if (!ASSERT_OK_PTR(skel, "xdp_vlan__open_and_load"))
|
||||
return;
|
||||
|
||||
if (test__start_subtest("0"))
|
||||
xdp_vlan(skel->progs.xdp_vlan_remove_outer2, skel->progs.tc_vlan_push, 0);
|
||||
|
||||
if (test__start_subtest("DRV_MODE"))
|
||||
xdp_vlan(skel->progs.xdp_vlan_remove_outer2, skel->progs.tc_vlan_push,
|
||||
XDP_FLAGS_DRV_MODE);
|
||||
|
||||
if (test__start_subtest("SKB_MODE"))
|
||||
xdp_vlan(skel->progs.xdp_vlan_remove_outer2, skel->progs.tc_vlan_push,
|
||||
XDP_FLAGS_SKB_MODE);
|
||||
|
||||
test_xdp_vlan__destroy(skel);
|
||||
}
|
||||
@@ -6,6 +6,8 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdatomic.h>
|
||||
#include "bpf_arena_common.h"
|
||||
#include "../../../include/linux/filter.h"
|
||||
#include "bpf_misc.h"
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_ARENA);
|
||||
@@ -19,9 +21,17 @@ struct {
|
||||
} arena SEC(".maps");
|
||||
|
||||
#if defined(ENABLE_ATOMICS_TESTS) && defined(__BPF_FEATURE_ADDR_SPACE_CAST)
|
||||
bool skip_tests __attribute((__section__(".data"))) = false;
|
||||
bool skip_all_tests __attribute((__section__(".data"))) = false;
|
||||
#else
|
||||
bool skip_tests = true;
|
||||
bool skip_all_tests = true;
|
||||
#endif
|
||||
|
||||
#if defined(ENABLE_ATOMICS_TESTS) && \
|
||||
defined(__BPF_FEATURE_ADDR_SPACE_CAST) && \
|
||||
(defined(__TARGET_ARCH_arm64) || defined(__TARGET_ARCH_x86))
|
||||
bool skip_lacq_srel_tests __attribute((__section__(".data"))) = false;
|
||||
#else
|
||||
bool skip_lacq_srel_tests = true;
|
||||
#endif
|
||||
|
||||
__u32 pid = 0;
|
||||
@@ -274,4 +284,111 @@ int uaf(const void *ctx)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if __clang_major__ >= 18
|
||||
__u8 __arena_global load_acquire8_value = 0x12;
|
||||
__u16 __arena_global load_acquire16_value = 0x1234;
|
||||
__u32 __arena_global load_acquire32_value = 0x12345678;
|
||||
__u64 __arena_global load_acquire64_value = 0x1234567890abcdef;
|
||||
|
||||
__u8 __arena_global load_acquire8_result = 0;
|
||||
__u16 __arena_global load_acquire16_result = 0;
|
||||
__u32 __arena_global load_acquire32_result = 0;
|
||||
__u64 __arena_global load_acquire64_result = 0;
|
||||
#else
|
||||
/* clang-17 crashes if the .addr_space.1 ELF section has holes. Work around
|
||||
* this issue by defining the below variables as 64-bit.
|
||||
*/
|
||||
__u64 __arena_global load_acquire8_value;
|
||||
__u64 __arena_global load_acquire16_value;
|
||||
__u64 __arena_global load_acquire32_value;
|
||||
__u64 __arena_global load_acquire64_value;
|
||||
|
||||
__u64 __arena_global load_acquire8_result;
|
||||
__u64 __arena_global load_acquire16_result;
|
||||
__u64 __arena_global load_acquire32_result;
|
||||
__u64 __arena_global load_acquire64_result;
|
||||
#endif
|
||||
|
||||
SEC("raw_tp/sys_enter")
|
||||
int load_acquire(const void *ctx)
|
||||
{
|
||||
#if defined(ENABLE_ATOMICS_TESTS) && \
|
||||
defined(__BPF_FEATURE_ADDR_SPACE_CAST) && \
|
||||
(defined(__TARGET_ARCH_arm64) || defined(__TARGET_ARCH_x86))
|
||||
|
||||
#define LOAD_ACQUIRE_ARENA(SIZEOP, SIZE, SRC, DST) \
|
||||
{ asm volatile ( \
|
||||
"r1 = %[" #SRC "] ll;" \
|
||||
"r1 = addr_space_cast(r1, 0x0, 0x1);" \
|
||||
".8byte %[load_acquire_insn];" \
|
||||
"r3 = %[" #DST "] ll;" \
|
||||
"r3 = addr_space_cast(r3, 0x0, 0x1);" \
|
||||
"*(" #SIZE " *)(r3 + 0) = r2;" \
|
||||
: \
|
||||
: __imm_addr(SRC), \
|
||||
__imm_insn(load_acquire_insn, \
|
||||
BPF_ATOMIC_OP(BPF_##SIZEOP, BPF_LOAD_ACQ, \
|
||||
BPF_REG_2, BPF_REG_1, 0)), \
|
||||
__imm_addr(DST) \
|
||||
: __clobber_all); } \
|
||||
|
||||
LOAD_ACQUIRE_ARENA(B, u8, load_acquire8_value, load_acquire8_result)
|
||||
LOAD_ACQUIRE_ARENA(H, u16, load_acquire16_value,
|
||||
load_acquire16_result)
|
||||
LOAD_ACQUIRE_ARENA(W, u32, load_acquire32_value,
|
||||
load_acquire32_result)
|
||||
LOAD_ACQUIRE_ARENA(DW, u64, load_acquire64_value,
|
||||
load_acquire64_result)
|
||||
#undef LOAD_ACQUIRE_ARENA
|
||||
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if __clang_major__ >= 18
|
||||
__u8 __arena_global store_release8_result = 0;
|
||||
__u16 __arena_global store_release16_result = 0;
|
||||
__u32 __arena_global store_release32_result = 0;
|
||||
__u64 __arena_global store_release64_result = 0;
|
||||
#else
|
||||
/* clang-17 crashes if the .addr_space.1 ELF section has holes. Work around
|
||||
* this issue by defining the below variables as 64-bit.
|
||||
*/
|
||||
__u64 __arena_global store_release8_result;
|
||||
__u64 __arena_global store_release16_result;
|
||||
__u64 __arena_global store_release32_result;
|
||||
__u64 __arena_global store_release64_result;
|
||||
#endif
|
||||
|
||||
SEC("raw_tp/sys_enter")
|
||||
int store_release(const void *ctx)
|
||||
{
|
||||
#if defined(ENABLE_ATOMICS_TESTS) && \
|
||||
defined(__BPF_FEATURE_ADDR_SPACE_CAST) && \
|
||||
(defined(__TARGET_ARCH_arm64) || defined(__TARGET_ARCH_x86))
|
||||
|
||||
#define STORE_RELEASE_ARENA(SIZEOP, DST, VAL) \
|
||||
{ asm volatile ( \
|
||||
"r1 = " VAL ";" \
|
||||
"r2 = %[" #DST "] ll;" \
|
||||
"r2 = addr_space_cast(r2, 0x0, 0x1);" \
|
||||
".8byte %[store_release_insn];" \
|
||||
: \
|
||||
: __imm_addr(DST), \
|
||||
__imm_insn(store_release_insn, \
|
||||
BPF_ATOMIC_OP(BPF_##SIZEOP, BPF_STORE_REL, \
|
||||
BPF_REG_2, BPF_REG_1, 0)) \
|
||||
: __clobber_all); } \
|
||||
|
||||
STORE_RELEASE_ARENA(B, store_release8_result, "0x12")
|
||||
STORE_RELEASE_ARENA(H, store_release16_result, "0x1234")
|
||||
STORE_RELEASE_ARENA(W, store_release32_result, "0x12345678")
|
||||
STORE_RELEASE_ARENA(DW, store_release64_result,
|
||||
"0x1234567890abcdef ll")
|
||||
#undef STORE_RELEASE_ARENA
|
||||
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
51
tools/testing/selftests/bpf/progs/arena_spin_lock.c
Normal file
51
tools/testing/selftests/bpf/progs/arena_spin_lock.c
Normal file
@@ -0,0 +1,51 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */
|
||||
#include <vmlinux.h>
|
||||
#include <bpf/bpf_tracing.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include "bpf_misc.h"
|
||||
#include "bpf_arena_spin_lock.h"
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_ARENA);
|
||||
__uint(map_flags, BPF_F_MMAPABLE);
|
||||
__uint(max_entries, 100); /* number of pages */
|
||||
#ifdef __TARGET_ARCH_arm64
|
||||
__ulong(map_extra, 0x1ull << 32); /* start of mmap() region */
|
||||
#else
|
||||
__ulong(map_extra, 0x1ull << 44); /* start of mmap() region */
|
||||
#endif
|
||||
} arena SEC(".maps");
|
||||
|
||||
int cs_count;
|
||||
|
||||
#if defined(ENABLE_ATOMICS_TESTS) && defined(__BPF_FEATURE_ADDR_SPACE_CAST)
|
||||
arena_spinlock_t __arena lock;
|
||||
int test_skip = 1;
|
||||
#else
|
||||
int test_skip = 2;
|
||||
#endif
|
||||
|
||||
int counter;
|
||||
int limit;
|
||||
|
||||
SEC("tc")
|
||||
int prog(void *ctx)
|
||||
{
|
||||
int ret = -2;
|
||||
|
||||
#if defined(ENABLE_ATOMICS_TESTS) && defined(__BPF_FEATURE_ADDR_SPACE_CAST)
|
||||
unsigned long flags;
|
||||
|
||||
if ((ret = arena_spin_lock_irqsave(&lock, flags)))
|
||||
return ret;
|
||||
if (counter != limit)
|
||||
counter++;
|
||||
bpf_repeat(cs_count);
|
||||
ret = 0;
|
||||
arena_spin_unlock_irqrestore(&lock, flags);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
@@ -9,6 +9,13 @@ char _license[] SEC("license") = "GPL";
|
||||
uint32_t tid = 0;
|
||||
int num_unknown_tid = 0;
|
||||
int num_known_tid = 0;
|
||||
void *user_ptr = 0;
|
||||
void *user_ptr_long = 0;
|
||||
uint32_t pid = 0;
|
||||
|
||||
static char big_str1[5000];
|
||||
static char big_str2[5005];
|
||||
static char big_str3[4996];
|
||||
|
||||
SEC("iter/task")
|
||||
int dump_task(struct bpf_iter__task *ctx)
|
||||
@@ -35,7 +42,9 @@ int dump_task(struct bpf_iter__task *ctx)
|
||||
}
|
||||
|
||||
int num_expected_failure_copy_from_user_task = 0;
|
||||
int num_expected_failure_copy_from_user_task_str = 0;
|
||||
int num_success_copy_from_user_task = 0;
|
||||
int num_success_copy_from_user_task_str = 0;
|
||||
|
||||
SEC("iter.s/task")
|
||||
int dump_task_sleepable(struct bpf_iter__task *ctx)
|
||||
@@ -44,6 +53,9 @@ int dump_task_sleepable(struct bpf_iter__task *ctx)
|
||||
struct task_struct *task = ctx->task;
|
||||
static const char info[] = " === END ===";
|
||||
struct pt_regs *regs;
|
||||
char task_str1[10] = "aaaaaaaaaa";
|
||||
char task_str2[10], task_str3[10];
|
||||
char task_str4[20] = "aaaaaaaaaaaaaaaaaaaa";
|
||||
void *ptr;
|
||||
uint32_t user_data = 0;
|
||||
int ret;
|
||||
@@ -78,8 +90,106 @@ int dump_task_sleepable(struct bpf_iter__task *ctx)
|
||||
BPF_SEQ_PRINTF(seq, "%s\n", info);
|
||||
return 0;
|
||||
}
|
||||
|
||||
++num_success_copy_from_user_task;
|
||||
|
||||
/* Read an invalid pointer and ensure we get an error */
|
||||
ptr = NULL;
|
||||
ret = bpf_copy_from_user_task_str((char *)task_str1, sizeof(task_str1), ptr, task, 0);
|
||||
if (ret >= 0 || task_str1[9] != 'a' || task_str1[0] != '\0') {
|
||||
BPF_SEQ_PRINTF(seq, "%s\n", info);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Read an invalid pointer and ensure we get error with pad zeros flag */
|
||||
ptr = NULL;
|
||||
ret = bpf_copy_from_user_task_str((char *)task_str1, sizeof(task_str1),
|
||||
ptr, task, BPF_F_PAD_ZEROS);
|
||||
if (ret >= 0 || task_str1[9] != '\0' || task_str1[0] != '\0') {
|
||||
BPF_SEQ_PRINTF(seq, "%s\n", info);
|
||||
return 0;
|
||||
}
|
||||
|
||||
++num_expected_failure_copy_from_user_task_str;
|
||||
|
||||
/* Same length as the string */
|
||||
ret = bpf_copy_from_user_task_str((char *)task_str2, 10, user_ptr, task, 0);
|
||||
/* only need to do the task pid check once */
|
||||
if (bpf_strncmp(task_str2, 10, "test_data\0") != 0 || ret != 10 || task->tgid != pid) {
|
||||
BPF_SEQ_PRINTF(seq, "%s\n", info);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Shorter length than the string */
|
||||
ret = bpf_copy_from_user_task_str((char *)task_str3, 2, user_ptr, task, 0);
|
||||
if (bpf_strncmp(task_str3, 2, "t\0") != 0 || ret != 2) {
|
||||
BPF_SEQ_PRINTF(seq, "%s\n", info);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Longer length than the string */
|
||||
ret = bpf_copy_from_user_task_str((char *)task_str4, 20, user_ptr, task, 0);
|
||||
if (bpf_strncmp(task_str4, 10, "test_data\0") != 0 || ret != 10
|
||||
|| task_str4[sizeof(task_str4) - 1] != 'a') {
|
||||
BPF_SEQ_PRINTF(seq, "%s\n", info);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Longer length than the string with pad zeros flag */
|
||||
ret = bpf_copy_from_user_task_str((char *)task_str4, 20, user_ptr, task, BPF_F_PAD_ZEROS);
|
||||
if (bpf_strncmp(task_str4, 10, "test_data\0") != 0 || ret != 10
|
||||
|| task_str4[sizeof(task_str4) - 1] != '\0') {
|
||||
BPF_SEQ_PRINTF(seq, "%s\n", info);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Longer length than the string past a page boundary */
|
||||
ret = bpf_copy_from_user_task_str(big_str1, 5000, user_ptr, task, 0);
|
||||
if (bpf_strncmp(big_str1, 10, "test_data\0") != 0 || ret != 10) {
|
||||
BPF_SEQ_PRINTF(seq, "%s\n", info);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* String that crosses a page boundary */
|
||||
ret = bpf_copy_from_user_task_str(big_str1, 5000, user_ptr_long, task, BPF_F_PAD_ZEROS);
|
||||
if (bpf_strncmp(big_str1, 4, "baba") != 0 || ret != 5000
|
||||
|| bpf_strncmp(big_str1 + 4996, 4, "bab\0") != 0) {
|
||||
BPF_SEQ_PRINTF(seq, "%s\n", info);
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 4999; ++i) {
|
||||
if (i % 2 == 0) {
|
||||
if (big_str1[i] != 'b') {
|
||||
BPF_SEQ_PRINTF(seq, "%s\n", info);
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
if (big_str1[i] != 'a') {
|
||||
BPF_SEQ_PRINTF(seq, "%s\n", info);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Longer length than the string that crosses a page boundary */
|
||||
ret = bpf_copy_from_user_task_str(big_str2, 5005, user_ptr_long, task, BPF_F_PAD_ZEROS);
|
||||
if (bpf_strncmp(big_str2, 4, "baba") != 0 || ret != 5000
|
||||
|| bpf_strncmp(big_str2 + 4996, 5, "bab\0\0") != 0) {
|
||||
BPF_SEQ_PRINTF(seq, "%s\n", info);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Shorter length than the string that crosses a page boundary */
|
||||
ret = bpf_copy_from_user_task_str(big_str3, 4996, user_ptr_long, task, 0);
|
||||
if (bpf_strncmp(big_str3, 4, "baba") != 0 || ret != 4996
|
||||
|| bpf_strncmp(big_str3 + 4992, 4, "bab\0") != 0) {
|
||||
BPF_SEQ_PRINTF(seq, "%s\n", info);
|
||||
return 0;
|
||||
}
|
||||
|
||||
++num_success_copy_from_user_task_str;
|
||||
|
||||
if (ctx->meta->seq_num == 0)
|
||||
BPF_SEQ_PRINTF(seq, " tgid gid data\n");
|
||||
|
||||
|
||||
@@ -135,6 +135,8 @@
|
||||
#define __arch_arm64 __arch("ARM64")
|
||||
#define __arch_riscv64 __arch("RISCV64")
|
||||
#define __caps_unpriv(caps) __attribute__((btf_decl_tag("comment:test_caps_unpriv=" EXPAND_QUOTE(caps))))
|
||||
#define __load_if_JITed() __attribute__((btf_decl_tag("comment:load_mode=jited")))
|
||||
#define __load_if_no_JITed() __attribute__((btf_decl_tag("comment:load_mode=no_jited")))
|
||||
|
||||
/* Define common capabilities tested using __caps_unpriv */
|
||||
#define CAP_NET_ADMIN 12
|
||||
@@ -172,6 +174,9 @@
|
||||
#elif defined(__TARGET_ARCH_riscv)
|
||||
#define SYSCALL_WRAPPER 1
|
||||
#define SYS_PREFIX "__riscv_"
|
||||
#elif defined(__TARGET_ARCH_powerpc)
|
||||
#define SYSCALL_WRAPPER 1
|
||||
#define SYS_PREFIX ""
|
||||
#else
|
||||
#define SYSCALL_WRAPPER 0
|
||||
#define SYS_PREFIX "__se_"
|
||||
@@ -208,4 +213,21 @@
|
||||
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
|
||||
#endif
|
||||
|
||||
#if (defined(__TARGET_ARCH_arm64) || defined(__TARGET_ARCH_x86) || \
|
||||
(defined(__TARGET_ARCH_riscv) && __riscv_xlen == 64) || \
|
||||
defined(__TARGET_ARCH_arm) || defined(__TARGET_ARCH_s390) || \
|
||||
defined(__TARGET_ARCH_loongarch)) && \
|
||||
__clang_major__ >= 18
|
||||
#define CAN_USE_GOTOL
|
||||
#endif
|
||||
|
||||
#if _clang_major__ >= 18
|
||||
#define CAN_USE_BPF_ST
|
||||
#endif
|
||||
|
||||
#if __clang_major__ >= 18 && defined(ENABLE_ATOMICS_TESTS) && \
|
||||
(defined(__TARGET_ARCH_arm64) || defined(__TARGET_ARCH_x86))
|
||||
#define CAN_USE_LOAD_ACQ_STORE_REL
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -15,7 +15,11 @@
|
||||
#define SO_KEEPALIVE 9
|
||||
#define SO_PRIORITY 12
|
||||
#define SO_REUSEPORT 15
|
||||
#if defined(__TARGET_ARCH_powerpc)
|
||||
#define SO_RCVLOWAT 16
|
||||
#else
|
||||
#define SO_RCVLOWAT 18
|
||||
#endif
|
||||
#define SO_BINDTODEVICE 25
|
||||
#define SO_MARK 36
|
||||
#define SO_MAX_PACING_RATE 47
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_arrays___err_bad_signed_arr_elem_sz x) {}
|
||||
41
tools/testing/selftests/bpf/progs/cgroup_preorder.c
Normal file
41
tools/testing/selftests/bpf/progs/cgroup_preorder.c
Normal file
@@ -0,0 +1,41 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */
|
||||
#include <vmlinux.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
unsigned int idx;
|
||||
__u8 result[4];
|
||||
|
||||
SEC("cgroup/getsockopt")
|
||||
int child(struct bpf_sockopt *ctx)
|
||||
{
|
||||
if (idx < 4)
|
||||
result[idx++] = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
SEC("cgroup/getsockopt")
|
||||
int child_2(struct bpf_sockopt *ctx)
|
||||
{
|
||||
if (idx < 4)
|
||||
result[idx++] = 2;
|
||||
return 1;
|
||||
}
|
||||
|
||||
SEC("cgroup/getsockopt")
|
||||
int parent(struct bpf_sockopt *ctx)
|
||||
{
|
||||
if (idx < 4)
|
||||
result[idx++] = 3;
|
||||
return 1;
|
||||
}
|
||||
|
||||
SEC("cgroup/getsockopt")
|
||||
int parent_2(struct bpf_sockopt *ctx)
|
||||
{
|
||||
if (idx < 4)
|
||||
result[idx++] = 4;
|
||||
return 1;
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include <linux/bpf.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
|
||||
__noinline
|
||||
long changes_pkt_data(struct __sk_buff *sk)
|
||||
{
|
||||
return bpf_skb_pull_data(sk, 0);
|
||||
}
|
||||
|
||||
__noinline __weak
|
||||
long does_not_change_pkt_data(struct __sk_buff *sk)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("?tc")
|
||||
int main_with_subprogs(struct __sk_buff *sk)
|
||||
{
|
||||
changes_pkt_data(sk);
|
||||
does_not_change_pkt_data(sk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("?tc")
|
||||
int main_changes(struct __sk_buff *sk)
|
||||
{
|
||||
bpf_skb_pull_data(sk, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("?tc")
|
||||
int main_does_not_change(struct __sk_buff *sk)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
424
tools/testing/selftests/bpf/progs/compute_live_registers.c
Normal file
424
tools/testing/selftests/bpf/progs/compute_live_registers.c
Normal file
@@ -0,0 +1,424 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include <linux/bpf.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include "../../../include/linux/filter.h"
|
||||
#include "bpf_arena_common.h"
|
||||
#include "bpf_misc.h"
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||
__uint(max_entries, 1);
|
||||
__type(key, __u32);
|
||||
__type(value, __u64);
|
||||
} test_map SEC(".maps");
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_ARENA);
|
||||
__uint(map_flags, BPF_F_MMAPABLE);
|
||||
__uint(max_entries, 1);
|
||||
} arena SEC(".maps");
|
||||
|
||||
SEC("socket")
|
||||
__log_level(2)
|
||||
__msg(" 0: .......... (b7) r0 = 42")
|
||||
__msg(" 1: 0......... (bf) r1 = r0")
|
||||
__msg(" 2: .1........ (bf) r2 = r1")
|
||||
__msg(" 3: ..2....... (bf) r3 = r2")
|
||||
__msg(" 4: ...3...... (bf) r4 = r3")
|
||||
__msg(" 5: ....4..... (bf) r5 = r4")
|
||||
__msg(" 6: .....5.... (bf) r6 = r5")
|
||||
__msg(" 7: ......6... (bf) r7 = r6")
|
||||
__msg(" 8: .......7.. (bf) r8 = r7")
|
||||
__msg(" 9: ........8. (bf) r9 = r8")
|
||||
__msg("10: .........9 (bf) r0 = r9")
|
||||
__msg("11: 0......... (95) exit")
|
||||
__naked void assign_chain(void)
|
||||
{
|
||||
asm volatile (
|
||||
"r0 = 42;"
|
||||
"r1 = r0;"
|
||||
"r2 = r1;"
|
||||
"r3 = r2;"
|
||||
"r4 = r3;"
|
||||
"r5 = r4;"
|
||||
"r6 = r5;"
|
||||
"r7 = r6;"
|
||||
"r8 = r7;"
|
||||
"r9 = r8;"
|
||||
"r0 = r9;"
|
||||
"exit;"
|
||||
::: __clobber_all);
|
||||
}
|
||||
|
||||
SEC("socket")
|
||||
__log_level(2)
|
||||
__msg("0: .......... (b7) r1 = 7")
|
||||
__msg("1: .1........ (07) r1 += 7")
|
||||
__msg("2: .......... (b7) r2 = 7")
|
||||
__msg("3: ..2....... (b7) r3 = 42")
|
||||
__msg("4: ..23...... (0f) r2 += r3")
|
||||
__msg("5: .......... (b7) r0 = 0")
|
||||
__msg("6: 0......... (95) exit")
|
||||
__naked void arithmetics(void)
|
||||
{
|
||||
asm volatile (
|
||||
"r1 = 7;"
|
||||
"r1 += 7;"
|
||||
"r2 = 7;"
|
||||
"r3 = 42;"
|
||||
"r2 += r3;"
|
||||
"r0 = 0;"
|
||||
"exit;"
|
||||
::: __clobber_all);
|
||||
}
|
||||
|
||||
#ifdef CAN_USE_BPF_ST
|
||||
SEC("socket")
|
||||
__log_level(2)
|
||||
__msg(" 1: .1........ (07) r1 += -8")
|
||||
__msg(" 2: .1........ (7a) *(u64 *)(r1 +0) = 7")
|
||||
__msg(" 3: .1........ (b7) r2 = 42")
|
||||
__msg(" 4: .12....... (7b) *(u64 *)(r1 +0) = r2")
|
||||
__msg(" 5: .12....... (7b) *(u64 *)(r1 +0) = r2")
|
||||
__msg(" 6: .......... (b7) r0 = 0")
|
||||
__naked void store(void)
|
||||
{
|
||||
asm volatile (
|
||||
"r1 = r10;"
|
||||
"r1 += -8;"
|
||||
"*(u64 *)(r1 +0) = 7;"
|
||||
"r2 = 42;"
|
||||
"*(u64 *)(r1 +0) = r2;"
|
||||
"*(u64 *)(r1 +0) = r2;"
|
||||
"r0 = 0;"
|
||||
"exit;"
|
||||
::: __clobber_all);
|
||||
}
|
||||
#endif
|
||||
|
||||
SEC("socket")
|
||||
__log_level(2)
|
||||
__msg("1: ....4..... (07) r4 += -8")
|
||||
__msg("2: ....4..... (79) r5 = *(u64 *)(r4 +0)")
|
||||
__msg("3: ....45.... (07) r4 += -8")
|
||||
__naked void load(void)
|
||||
{
|
||||
asm volatile (
|
||||
"r4 = r10;"
|
||||
"r4 += -8;"
|
||||
"r5 = *(u64 *)(r4 +0);"
|
||||
"r4 += -8;"
|
||||
"r0 = r5;"
|
||||
"exit;"
|
||||
::: __clobber_all);
|
||||
}
|
||||
|
||||
SEC("socket")
|
||||
__log_level(2)
|
||||
__msg("0: .1........ (61) r2 = *(u32 *)(r1 +0)")
|
||||
__msg("1: ..2....... (d4) r2 = le64 r2")
|
||||
__msg("2: ..2....... (bf) r0 = r2")
|
||||
__naked void endian(void)
|
||||
{
|
||||
asm volatile (
|
||||
"r2 = *(u32 *)(r1 +0);"
|
||||
"r2 = le64 r2;"
|
||||
"r0 = r2;"
|
||||
"exit;"
|
||||
::: __clobber_all);
|
||||
}
|
||||
|
||||
SEC("socket")
|
||||
__log_level(2)
|
||||
__msg(" 8: 0......... (b7) r1 = 1")
|
||||
__msg(" 9: 01........ (db) r1 = atomic64_fetch_add((u64 *)(r0 +0), r1)")
|
||||
__msg("10: 01........ (c3) lock *(u32 *)(r0 +0) += r1")
|
||||
__msg("11: 01........ (db) r1 = atomic64_xchg((u64 *)(r0 +0), r1)")
|
||||
__msg("12: 01........ (bf) r2 = r0")
|
||||
__msg("13: .12....... (bf) r0 = r1")
|
||||
__msg("14: 012....... (db) r0 = atomic64_cmpxchg((u64 *)(r2 +0), r0, r1)")
|
||||
__naked void atomic(void)
|
||||
{
|
||||
asm volatile (
|
||||
"r2 = r10;"
|
||||
"r2 += -8;"
|
||||
"r1 = 0;"
|
||||
"*(u64 *)(r2 +0) = r1;"
|
||||
"r1 = %[test_map] ll;"
|
||||
"call %[bpf_map_lookup_elem];"
|
||||
"if r0 == 0 goto 1f;"
|
||||
"r1 = 1;"
|
||||
"r1 = atomic_fetch_add((u64 *)(r0 +0), r1);"
|
||||
".8byte %[add_nofetch];" /* same as "lock *(u32 *)(r0 +0) += r1;" */
|
||||
"r1 = xchg_64(r0 + 0, r1);"
|
||||
"r2 = r0;"
|
||||
"r0 = r1;"
|
||||
"r0 = cmpxchg_64(r2 + 0, r0, r1);"
|
||||
"1: exit;"
|
||||
:
|
||||
: __imm(bpf_map_lookup_elem),
|
||||
__imm_addr(test_map),
|
||||
__imm_insn(add_nofetch, BPF_ATOMIC_OP(BPF_W, BPF_ADD, BPF_REG_0, BPF_REG_1, 0))
|
||||
: __clobber_all);
|
||||
}
|
||||
|
||||
#ifdef CAN_USE_LOAD_ACQ_STORE_REL
|
||||
|
||||
SEC("socket")
|
||||
__log_level(2)
|
||||
__msg("2: .12....... (db) store_release((u64 *)(r2 -8), r1)")
|
||||
__msg("3: .......... (bf) r3 = r10")
|
||||
__msg("4: ...3...... (db) r4 = load_acquire((u64 *)(r3 -8))")
|
||||
__naked void atomic_load_acq_store_rel(void)
|
||||
{
|
||||
asm volatile (
|
||||
"r1 = 42;"
|
||||
"r2 = r10;"
|
||||
".8byte %[store_release_insn];" /* store_release((u64 *)(r2 - 8), r1); */
|
||||
"r3 = r10;"
|
||||
".8byte %[load_acquire_insn];" /* r4 = load_acquire((u64 *)(r3 + 0)); */
|
||||
"r0 = r4;"
|
||||
"exit;"
|
||||
:
|
||||
: __imm_insn(store_release_insn,
|
||||
BPF_ATOMIC_OP(BPF_DW, BPF_STORE_REL, BPF_REG_2, BPF_REG_1, -8)),
|
||||
__imm_insn(load_acquire_insn,
|
||||
BPF_ATOMIC_OP(BPF_DW, BPF_LOAD_ACQ, BPF_REG_4, BPF_REG_3, -8))
|
||||
: __clobber_all);
|
||||
}
|
||||
|
||||
#endif /* CAN_USE_LOAD_ACQ_STORE_REL */
|
||||
|
||||
SEC("socket")
|
||||
__log_level(2)
|
||||
__msg("4: .12....7.. (85) call bpf_trace_printk#6")
|
||||
__msg("5: 0......7.. (0f) r0 += r7")
|
||||
__naked void regular_call(void)
|
||||
{
|
||||
asm volatile (
|
||||
"r7 = 1;"
|
||||
"r1 = r10;"
|
||||
"r1 += -8;"
|
||||
"r2 = 1;"
|
||||
"call %[bpf_trace_printk];"
|
||||
"r0 += r7;"
|
||||
"exit;"
|
||||
:
|
||||
: __imm(bpf_trace_printk)
|
||||
: __clobber_all);
|
||||
}
|
||||
|
||||
SEC("socket")
|
||||
__log_level(2)
|
||||
__msg("2: 012....... (25) if r1 > 0x7 goto pc+1")
|
||||
__msg("3: ..2....... (bf) r0 = r2")
|
||||
__naked void if1(void)
|
||||
{
|
||||
asm volatile (
|
||||
"r0 = 1;"
|
||||
"r2 = 2;"
|
||||
"if r1 > 0x7 goto +1;"
|
||||
"r0 = r2;"
|
||||
"exit;"
|
||||
::: __clobber_all);
|
||||
}
|
||||
|
||||
SEC("socket")
|
||||
__log_level(2)
|
||||
__msg("3: 0123...... (2d) if r1 > r3 goto pc+1")
|
||||
__msg("4: ..2....... (bf) r0 = r2")
|
||||
__naked void if2(void)
|
||||
{
|
||||
asm volatile (
|
||||
"r0 = 1;"
|
||||
"r2 = 2;"
|
||||
"r3 = 7;"
|
||||
"if r1 > r3 goto +1;"
|
||||
"r0 = r2;"
|
||||
"exit;"
|
||||
::: __clobber_all);
|
||||
}
|
||||
|
||||
SEC("socket")
|
||||
__log_level(2)
|
||||
__msg("0: .......... (b7) r1 = 0")
|
||||
__msg("1: .1........ (b7) r2 = 7")
|
||||
__msg("2: .12....... (25) if r1 > 0x7 goto pc+4")
|
||||
__msg("3: .12....... (07) r1 += 1")
|
||||
__msg("4: .12....... (27) r2 *= 2")
|
||||
__msg("5: .12....... (05) goto pc+0")
|
||||
__msg("6: .12....... (05) goto pc-5")
|
||||
__msg("7: .......... (b7) r0 = 0")
|
||||
__msg("8: 0......... (95) exit")
|
||||
__naked void loop(void)
|
||||
{
|
||||
asm volatile (
|
||||
"r1 = 0;"
|
||||
"r2 = 7;"
|
||||
"if r1 > 0x7 goto +4;"
|
||||
"r1 += 1;"
|
||||
"r2 *= 2;"
|
||||
"goto +0;"
|
||||
"goto -5;"
|
||||
"r0 = 0;"
|
||||
"exit;"
|
||||
:
|
||||
: __imm(bpf_trace_printk)
|
||||
: __clobber_all);
|
||||
}
|
||||
|
||||
#ifdef CAN_USE_GOTOL
|
||||
SEC("socket")
|
||||
__log_level(2)
|
||||
__msg("2: .123...... (25) if r1 > 0x7 goto pc+2")
|
||||
__msg("3: ..2....... (bf) r0 = r2")
|
||||
__msg("4: 0......... (06) gotol pc+1")
|
||||
__msg("5: ...3...... (bf) r0 = r3")
|
||||
__msg("6: 0......... (95) exit")
|
||||
__naked void gotol(void)
|
||||
{
|
||||
asm volatile (
|
||||
"r2 = 42;"
|
||||
"r3 = 24;"
|
||||
"if r1 > 0x7 goto +2;"
|
||||
"r0 = r2;"
|
||||
"gotol +1;"
|
||||
"r0 = r3;"
|
||||
"exit;"
|
||||
:
|
||||
: __imm(bpf_trace_printk)
|
||||
: __clobber_all);
|
||||
}
|
||||
#endif
|
||||
|
||||
SEC("socket")
|
||||
__log_level(2)
|
||||
__msg("0: .......... (b7) r1 = 1")
|
||||
__msg("1: .1........ (e5) may_goto pc+1")
|
||||
__msg("2: .......... (05) goto pc-3")
|
||||
__msg("3: .1........ (bf) r0 = r1")
|
||||
__msg("4: 0......... (95) exit")
|
||||
__naked void may_goto(void)
|
||||
{
|
||||
asm volatile (
|
||||
"1: r1 = 1;"
|
||||
".8byte %[may_goto];"
|
||||
"goto 1b;"
|
||||
"r0 = r1;"
|
||||
"exit;"
|
||||
:
|
||||
: __imm(bpf_get_smp_processor_id),
|
||||
__imm_insn(may_goto, BPF_RAW_INSN(BPF_JMP | BPF_JCOND, 0, 0, +1 /* offset */, 0))
|
||||
: __clobber_all);
|
||||
}
|
||||
|
||||
SEC("socket")
|
||||
__log_level(2)
|
||||
__msg("1: 0......... (18) r2 = 0x7")
|
||||
__msg("3: 0.2....... (0f) r0 += r2")
|
||||
__naked void ldimm64(void)
|
||||
{
|
||||
asm volatile (
|
||||
"r0 = 0;"
|
||||
"r2 = 0x7 ll;"
|
||||
"r0 += r2;"
|
||||
"exit;"
|
||||
:
|
||||
:: __clobber_all);
|
||||
}
|
||||
|
||||
/* No rules specific for LD_ABS/LD_IND, default behaviour kicks in */
|
||||
SEC("socket")
|
||||
__log_level(2)
|
||||
__msg("2: 0123456789 (30) r0 = *(u8 *)skb[42]")
|
||||
__msg("3: 012.456789 (0f) r7 += r0")
|
||||
__msg("4: 012.456789 (b7) r3 = 42")
|
||||
__msg("5: 0123456789 (50) r0 = *(u8 *)skb[r3 + 0]")
|
||||
__msg("6: 0......7.. (0f) r7 += r0")
|
||||
__naked void ldabs(void)
|
||||
{
|
||||
asm volatile (
|
||||
"r6 = r1;"
|
||||
"r7 = 0;"
|
||||
"r0 = *(u8 *)skb[42];"
|
||||
"r7 += r0;"
|
||||
"r3 = 42;"
|
||||
".8byte %[ld_ind];" /* same as "r0 = *(u8 *)skb[r3];" */
|
||||
"r7 += r0;"
|
||||
"r0 = r7;"
|
||||
"exit;"
|
||||
:
|
||||
: __imm_insn(ld_ind, BPF_LD_IND(BPF_B, BPF_REG_3, 0))
|
||||
: __clobber_all);
|
||||
}
|
||||
|
||||
|
||||
#ifdef __BPF_FEATURE_ADDR_SPACE_CAST
|
||||
SEC("?fentry.s/" SYS_PREFIX "sys_getpgid")
|
||||
__log_level(2)
|
||||
__msg(" 6: .12345.... (85) call bpf_arena_alloc_pages")
|
||||
__msg(" 7: 0......... (bf) r1 = addr_space_cast(r0, 0, 1)")
|
||||
__msg(" 8: .1........ (b7) r2 = 42")
|
||||
__naked void addr_space_cast(void)
|
||||
{
|
||||
asm volatile (
|
||||
"r1 = %[arena] ll;"
|
||||
"r2 = 0;"
|
||||
"r3 = 1;"
|
||||
"r4 = 0;"
|
||||
"r5 = 0;"
|
||||
"call %[bpf_arena_alloc_pages];"
|
||||
"r1 = addr_space_cast(r0, 0, 1);"
|
||||
"r2 = 42;"
|
||||
"*(u64 *)(r1 +0) = r2;"
|
||||
"r0 = 0;"
|
||||
"exit;"
|
||||
:
|
||||
: __imm(bpf_arena_alloc_pages),
|
||||
__imm_addr(arena)
|
||||
: __clobber_all);
|
||||
}
|
||||
#endif
|
||||
|
||||
static __used __naked int aux1(void)
|
||||
{
|
||||
asm volatile (
|
||||
"r0 = r1;"
|
||||
"r0 += r2;"
|
||||
"exit;"
|
||||
::: __clobber_all);
|
||||
}
|
||||
|
||||
SEC("socket")
|
||||
__log_level(2)
|
||||
__msg("0: ....45.... (b7) r1 = 1")
|
||||
__msg("1: .1..45.... (b7) r2 = 2")
|
||||
__msg("2: .12.45.... (b7) r3 = 3")
|
||||
/* Conservative liveness for subprog parameters. */
|
||||
__msg("3: .12345.... (85) call pc+2")
|
||||
__msg("4: .......... (b7) r0 = 0")
|
||||
__msg("5: 0......... (95) exit")
|
||||
__msg("6: .12....... (bf) r0 = r1")
|
||||
__msg("7: 0.2....... (0f) r0 += r2")
|
||||
/* Conservative liveness for subprog return value. */
|
||||
__msg("8: 0......... (95) exit")
|
||||
__naked void subprog1(void)
|
||||
{
|
||||
asm volatile (
|
||||
"r1 = 1;"
|
||||
"r2 = 2;"
|
||||
"r3 = 3;"
|
||||
"call aux1;"
|
||||
"r0 = 0;"
|
||||
"exit;"
|
||||
::: __clobber_all);
|
||||
}
|
||||
|
||||
/* to retain debug info for BTF generation */
|
||||
void kfunc_root(void)
|
||||
{
|
||||
bpf_arena_alloc_pages(0, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
@@ -13,12 +13,14 @@
|
||||
#define VERDICT_REJECT 0
|
||||
#define VERDICT_PROCEED 1
|
||||
|
||||
int port;
|
||||
|
||||
SEC("cgroup/connect4")
|
||||
int connect_v4_dropper(struct bpf_sock_addr *ctx)
|
||||
{
|
||||
if (ctx->type != SOCK_STREAM)
|
||||
return VERDICT_PROCEED;
|
||||
if (ctx->user_port == bpf_htons(60120))
|
||||
if (ctx->user_port == bpf_htons(port))
|
||||
return VERDICT_REJECT;
|
||||
return VERDICT_PROCEED;
|
||||
}
|
||||
|
||||
@@ -347,6 +347,7 @@ struct core_reloc_nesting___err_too_deep {
|
||||
*/
|
||||
struct core_reloc_arrays_output {
|
||||
int a2;
|
||||
int a3;
|
||||
char b123;
|
||||
int c1c;
|
||||
int d00d;
|
||||
@@ -455,6 +456,15 @@ struct core_reloc_arrays___err_bad_zero_sz_arr {
|
||||
struct core_reloc_arrays_substruct d[1][2];
|
||||
};
|
||||
|
||||
struct core_reloc_arrays___err_bad_signed_arr_elem_sz {
|
||||
/* int -> short (signed!): not supported case */
|
||||
short a[5];
|
||||
char b[2][3][4];
|
||||
struct core_reloc_arrays_substruct c[3];
|
||||
struct core_reloc_arrays_substruct d[1][2];
|
||||
struct core_reloc_arrays_substruct f[][2];
|
||||
};
|
||||
|
||||
/*
|
||||
* PRIMITIVES
|
||||
*/
|
||||
|
||||
@@ -61,6 +61,7 @@ u32 bpf_cpumask_any_distribute(const struct cpumask *src) __ksym __weak;
|
||||
u32 bpf_cpumask_any_and_distribute(const struct cpumask *src1,
|
||||
const struct cpumask *src2) __ksym __weak;
|
||||
u32 bpf_cpumask_weight(const struct cpumask *cpumask) __ksym __weak;
|
||||
int bpf_cpumask_populate(struct cpumask *cpumask, void *src, size_t src__sz) __ksym __weak;
|
||||
|
||||
void bpf_rcu_read_lock(void) __ksym __weak;
|
||||
void bpf_rcu_read_unlock(void) __ksym __weak;
|
||||
|
||||
@@ -222,3 +222,41 @@ int BPF_PROG(test_invalid_nested_array, struct task_struct *task, u64 clone_flag
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("tp_btf/task_newtask")
|
||||
__failure __msg("type=scalar expected=fp")
|
||||
int BPF_PROG(test_populate_invalid_destination, struct task_struct *task, u64 clone_flags)
|
||||
{
|
||||
struct bpf_cpumask *invalid = (struct bpf_cpumask *)0x123456;
|
||||
u64 bits;
|
||||
int ret;
|
||||
|
||||
ret = bpf_cpumask_populate((struct cpumask *)invalid, &bits, sizeof(bits));
|
||||
if (!ret)
|
||||
err = 2;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("tp_btf/task_newtask")
|
||||
__failure __msg("leads to invalid memory access")
|
||||
int BPF_PROG(test_populate_invalid_source, struct task_struct *task, u64 clone_flags)
|
||||
{
|
||||
void *garbage = (void *)0x123456;
|
||||
struct bpf_cpumask *local;
|
||||
int ret;
|
||||
|
||||
local = create_cpumask();
|
||||
if (!local) {
|
||||
err = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = bpf_cpumask_populate((struct cpumask *)local, garbage, 8);
|
||||
if (!ret)
|
||||
err = 2;
|
||||
|
||||
bpf_cpumask_release(local);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -749,7 +749,6 @@ out:
|
||||
}
|
||||
|
||||
SEC("tp_btf/task_newtask")
|
||||
__success
|
||||
int BPF_PROG(test_refcount_null_tracking, struct task_struct *task, u64 clone_flags)
|
||||
{
|
||||
struct bpf_cpumask *mask1, *mask2;
|
||||
@@ -770,3 +769,122 @@ free_masks_return:
|
||||
bpf_cpumask_release(mask2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("tp_btf/task_newtask")
|
||||
int BPF_PROG(test_populate_reject_small_mask, struct task_struct *task, u64 clone_flags)
|
||||
{
|
||||
struct bpf_cpumask *local;
|
||||
u8 toofewbits;
|
||||
int ret;
|
||||
|
||||
if (!is_test_task())
|
||||
return 0;
|
||||
|
||||
local = create_cpumask();
|
||||
if (!local)
|
||||
return 0;
|
||||
|
||||
/* The kfunc should prevent this operation */
|
||||
ret = bpf_cpumask_populate((struct cpumask *)local, &toofewbits, sizeof(toofewbits));
|
||||
if (ret != -EACCES)
|
||||
err = 2;
|
||||
|
||||
bpf_cpumask_release(local);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Mask is guaranteed to be large enough for bpf_cpumask_t. */
|
||||
#define CPUMASK_TEST_MASKLEN (sizeof(cpumask_t))
|
||||
|
||||
/* Add an extra word for the test_populate_reject_unaligned test. */
|
||||
u64 bits[CPUMASK_TEST_MASKLEN / 8 + 1];
|
||||
extern bool CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS __kconfig __weak;
|
||||
|
||||
SEC("tp_btf/task_newtask")
|
||||
int BPF_PROG(test_populate_reject_unaligned, struct task_struct *task, u64 clone_flags)
|
||||
{
|
||||
struct bpf_cpumask *mask;
|
||||
char *src;
|
||||
int ret;
|
||||
|
||||
if (!is_test_task())
|
||||
return 0;
|
||||
|
||||
/* Skip if unaligned accesses are fine for this arch. */
|
||||
if (CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)
|
||||
return 0;
|
||||
|
||||
mask = bpf_cpumask_create();
|
||||
if (!mask) {
|
||||
err = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Misalign the source array by a byte. */
|
||||
src = &((char *)bits)[1];
|
||||
|
||||
ret = bpf_cpumask_populate((struct cpumask *)mask, src, CPUMASK_TEST_MASKLEN);
|
||||
if (ret != -EINVAL)
|
||||
err = 2;
|
||||
|
||||
bpf_cpumask_release(mask);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
SEC("tp_btf/task_newtask")
|
||||
int BPF_PROG(test_populate, struct task_struct *task, u64 clone_flags)
|
||||
{
|
||||
struct bpf_cpumask *mask;
|
||||
bool bit;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
if (!is_test_task())
|
||||
return 0;
|
||||
|
||||
/* Set only odd bits. */
|
||||
__builtin_memset(bits, 0xaa, CPUMASK_TEST_MASKLEN);
|
||||
|
||||
mask = bpf_cpumask_create();
|
||||
if (!mask) {
|
||||
err = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Pass the entire bits array, the kfunc will only copy the valid bits. */
|
||||
ret = bpf_cpumask_populate((struct cpumask *)mask, bits, CPUMASK_TEST_MASKLEN);
|
||||
if (ret) {
|
||||
err = 2;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Test is there to appease the verifier. We cannot directly
|
||||
* access NR_CPUS, the upper bound for nr_cpus, so we infer
|
||||
* it from the size of cpumask_t.
|
||||
*/
|
||||
if (nr_cpus < 0 || nr_cpus >= CPUMASK_TEST_MASKLEN * 8) {
|
||||
err = 3;
|
||||
goto out;
|
||||
}
|
||||
|
||||
bpf_for(i, 0, nr_cpus) {
|
||||
/* Odd-numbered bits should be set, even ones unset. */
|
||||
bit = bpf_cpumask_test_cpu(i, (const struct cpumask *)mask);
|
||||
if (bit == (i % 2 != 0))
|
||||
continue;
|
||||
|
||||
err = 4;
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
bpf_cpumask_release(mask);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#undef CPUMASK_TEST_MASKLEN
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2022 Facebook */
|
||||
|
||||
#include <vmlinux.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <linux/bpf.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include <bpf/bpf_tracing.h>
|
||||
#include "bpf_misc.h"
|
||||
#include "bpf_kfuncs.h"
|
||||
#include "errno.h"
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
int pid, err, val;
|
||||
|
||||
struct sample {
|
||||
struct ringbuf_sample {
|
||||
int pid;
|
||||
int seq;
|
||||
long value;
|
||||
@@ -121,7 +120,7 @@ int test_dynptr_data(void *ctx)
|
||||
|
||||
static int ringbuf_callback(__u32 index, void *data)
|
||||
{
|
||||
struct sample *sample;
|
||||
struct ringbuf_sample *sample;
|
||||
|
||||
struct bpf_dynptr *ptr = (struct bpf_dynptr *)data;
|
||||
|
||||
@@ -138,7 +137,7 @@ SEC("?tp/syscalls/sys_enter_nanosleep")
|
||||
int test_ringbuf(void *ctx)
|
||||
{
|
||||
struct bpf_dynptr ptr;
|
||||
struct sample *sample;
|
||||
struct ringbuf_sample *sample;
|
||||
|
||||
if (bpf_get_current_pid_tgid() >> 32 != pid)
|
||||
return 0;
|
||||
@@ -567,3 +566,117 @@ int BPF_PROG(test_dynptr_skb_tp_btf, void *skb, void *location)
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline int bpf_memcmp(const char *a, const char *b, u32 size)
|
||||
{
|
||||
int i;
|
||||
|
||||
bpf_for(i, 0, size) {
|
||||
if (a[i] != b[i])
|
||||
return a[i] < b[i] ? -1 : 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("?tp/syscalls/sys_enter_nanosleep")
|
||||
int test_dynptr_copy(void *ctx)
|
||||
{
|
||||
char data[] = "hello there, world!!";
|
||||
char buf[32] = {'\0'};
|
||||
__u32 sz = sizeof(data);
|
||||
struct bpf_dynptr src, dst;
|
||||
|
||||
bpf_ringbuf_reserve_dynptr(&ringbuf, sz, 0, &src);
|
||||
bpf_ringbuf_reserve_dynptr(&ringbuf, sz, 0, &dst);
|
||||
|
||||
/* Test basic case of copying contiguous memory backed dynptrs */
|
||||
err = bpf_dynptr_write(&src, 0, data, sz, 0);
|
||||
err = err ?: bpf_dynptr_copy(&dst, 0, &src, 0, sz);
|
||||
err = err ?: bpf_dynptr_read(buf, sz, &dst, 0, 0);
|
||||
err = err ?: bpf_memcmp(data, buf, sz);
|
||||
|
||||
/* Test that offsets are handled correctly */
|
||||
err = err ?: bpf_dynptr_copy(&dst, 3, &src, 5, sz - 5);
|
||||
err = err ?: bpf_dynptr_read(buf, sz - 5, &dst, 3, 0);
|
||||
err = err ?: bpf_memcmp(data + 5, buf, sz - 5);
|
||||
|
||||
bpf_ringbuf_discard_dynptr(&src, 0);
|
||||
bpf_ringbuf_discard_dynptr(&dst, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("xdp")
|
||||
int test_dynptr_copy_xdp(struct xdp_md *xdp)
|
||||
{
|
||||
struct bpf_dynptr ptr_buf, ptr_xdp;
|
||||
char data[] = "qwertyuiopasdfghjkl";
|
||||
char buf[32] = {'\0'};
|
||||
__u32 len = sizeof(data);
|
||||
int i, chunks = 200;
|
||||
|
||||
/* ptr_xdp is backed by non-contiguous memory */
|
||||
bpf_dynptr_from_xdp(xdp, 0, &ptr_xdp);
|
||||
bpf_ringbuf_reserve_dynptr(&ringbuf, len * chunks, 0, &ptr_buf);
|
||||
|
||||
/* Destination dynptr is backed by non-contiguous memory */
|
||||
bpf_for(i, 0, chunks) {
|
||||
err = bpf_dynptr_write(&ptr_buf, i * len, data, len, 0);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = bpf_dynptr_copy(&ptr_xdp, 0, &ptr_buf, 0, len * chunks);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
bpf_for(i, 0, chunks) {
|
||||
__builtin_memset(buf, 0, sizeof(buf));
|
||||
err = bpf_dynptr_read(&buf, len, &ptr_xdp, i * len, 0);
|
||||
if (err)
|
||||
goto out;
|
||||
if (bpf_memcmp(data, buf, len) != 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Source dynptr is backed by non-contiguous memory */
|
||||
__builtin_memset(buf, 0, sizeof(buf));
|
||||
bpf_for(i, 0, chunks) {
|
||||
err = bpf_dynptr_write(&ptr_buf, i * len, buf, len, 0);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = bpf_dynptr_copy(&ptr_buf, 0, &ptr_xdp, 0, len * chunks);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
bpf_for(i, 0, chunks) {
|
||||
__builtin_memset(buf, 0, sizeof(buf));
|
||||
err = bpf_dynptr_read(&buf, len, &ptr_buf, i * len, 0);
|
||||
if (err)
|
||||
goto out;
|
||||
if (bpf_memcmp(data, buf, len) != 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Both source and destination dynptrs are backed by non-contiguous memory */
|
||||
err = bpf_dynptr_copy(&ptr_xdp, 2, &ptr_xdp, len, len * (chunks - 1));
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
bpf_for(i, 0, chunks - 1) {
|
||||
__builtin_memset(buf, 0, sizeof(buf));
|
||||
err = bpf_dynptr_read(&buf, len, &ptr_xdp, 2 + i * len, 0);
|
||||
if (err)
|
||||
goto out;
|
||||
if (bpf_memcmp(data, buf, len) != 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (bpf_dynptr_copy(&ptr_xdp, 2000, &ptr_xdp, 0, len * chunks) != -E2BIG)
|
||||
err = 1;
|
||||
|
||||
out:
|
||||
bpf_ringbuf_discard_dynptr(&ptr_buf, 0);
|
||||
return XDP_DROP;
|
||||
}
|
||||
|
||||
15
tools/testing/selftests/bpf/progs/fexit_noreturns.c
Normal file
15
tools/testing/selftests/bpf/progs/fexit_noreturns.c
Normal file
@@ -0,0 +1,15 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include <linux/bpf.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include <bpf/bpf_tracing.h>
|
||||
#include "bpf_misc.h"
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
SEC("fexit/do_exit")
|
||||
__failure __msg("Attaching fexit/fmod_ret to __noreturn functions is rejected.")
|
||||
int BPF_PROG(noreturns)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@@ -222,7 +222,7 @@ int __noinline global_local_irq_balance(void)
|
||||
}
|
||||
|
||||
SEC("?tc")
|
||||
__failure __msg("global function calls are not allowed with IRQs disabled")
|
||||
__success
|
||||
int irq_global_subprog(struct __sk_buff *ctx)
|
||||
{
|
||||
unsigned long flags;
|
||||
@@ -441,4 +441,73 @@ int irq_ooo_refs_array(struct __sk_buff *ctx)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __noinline
|
||||
global_subprog(int i)
|
||||
{
|
||||
if (i)
|
||||
bpf_printk("%p", &i);
|
||||
return i;
|
||||
}
|
||||
|
||||
int __noinline
|
||||
global_sleepable_helper_subprog(int i)
|
||||
{
|
||||
if (i)
|
||||
bpf_copy_from_user(&i, sizeof(i), NULL);
|
||||
return i;
|
||||
}
|
||||
|
||||
int __noinline
|
||||
global_sleepable_kfunc_subprog(int i)
|
||||
{
|
||||
if (i)
|
||||
bpf_copy_from_user_str(&i, sizeof(i), NULL, 0);
|
||||
global_subprog(i);
|
||||
return i;
|
||||
}
|
||||
|
||||
int __noinline
|
||||
global_subprog_calling_sleepable_global(int i)
|
||||
{
|
||||
if (!i)
|
||||
global_sleepable_kfunc_subprog(i);
|
||||
return i;
|
||||
}
|
||||
|
||||
SEC("?syscall")
|
||||
__success
|
||||
int irq_non_sleepable_global_subprog(void *ctx)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
bpf_local_irq_save(&flags);
|
||||
global_subprog(0);
|
||||
bpf_local_irq_restore(&flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("?syscall")
|
||||
__failure __msg("global functions that may sleep are not allowed in non-sleepable context")
|
||||
int irq_sleepable_helper_global_subprog(void *ctx)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
bpf_local_irq_save(&flags);
|
||||
global_sleepable_helper_subprog(0);
|
||||
bpf_local_irq_restore(&flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("?syscall")
|
||||
__failure __msg("global functions that may sleep are not allowed in non-sleepable context")
|
||||
int irq_sleepable_global_subprog_indirect(void *ctx)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
bpf_local_irq_save(&flags);
|
||||
global_subprog_calling_sleepable_global(0);
|
||||
bpf_local_irq_restore(&flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
#include "bpf_misc.h"
|
||||
#include "bpf_compiler.h"
|
||||
|
||||
#define unlikely(x) __builtin_expect(!!(x), 0)
|
||||
|
||||
static volatile int zero = 0;
|
||||
|
||||
int my_pid;
|
||||
@@ -1174,6 +1176,122 @@ __naked int loop_state_deps2(void)
|
||||
);
|
||||
}
|
||||
|
||||
SEC("?raw_tp")
|
||||
__failure
|
||||
__msg("math between fp pointer and register with unbounded")
|
||||
__flag(BPF_F_TEST_STATE_FREQ)
|
||||
__naked int loop_state_deps3(void)
|
||||
{
|
||||
/* This is equivalent to a C program below.
|
||||
*
|
||||
* if (random() != 24) { // assume false branch is placed first
|
||||
* i = iter_new(); // fp[-8]
|
||||
* while (iter_next(i));
|
||||
* iter_destroy(i);
|
||||
* return;
|
||||
* }
|
||||
*
|
||||
* for (i = 10; i > 0; i--); // increase dfs_depth for child states
|
||||
*
|
||||
* i = iter_new(); // fp[-8]
|
||||
* b = -24; // r8
|
||||
* for (;;) { // checkpoint (L)
|
||||
* if (iter_next(i)) // checkpoint (N)
|
||||
* break;
|
||||
* if (random() == 77) { // assume false branch is placed first
|
||||
* *(u64 *)(r10 + b) = 7; // this is not safe when b == -25
|
||||
* iter_destroy(i);
|
||||
* return;
|
||||
* }
|
||||
* if (random() == 42) { // assume false branch is placed first
|
||||
* b = -25;
|
||||
* }
|
||||
* }
|
||||
* iter_destroy(i);
|
||||
*
|
||||
* In case of a buggy verifier first loop might poison
|
||||
* env->cur_state->loop_entry with a state having 0 branches
|
||||
* and small dfs_depth. This would trigger NOT_EXACT states
|
||||
* comparison for some states within second loop.
|
||||
* Specifically, checkpoint (L) might be problematic if:
|
||||
* - branch with '*(u64 *)(r10 + b) = 7' is not explored yet;
|
||||
* - checkpoint (L) is first reached in state {b=-24};
|
||||
* - traversal is pruned at checkpoint (N) setting checkpoint's (L)
|
||||
* branch count to 0, thus making it eligible for use in pruning;
|
||||
* - checkpoint (L) is next reached in state {b=-25},
|
||||
* this would cause NOT_EXACT comparison with a state {b=-24}
|
||||
* while 'b' is not marked precise yet.
|
||||
*/
|
||||
asm volatile (
|
||||
"call %[bpf_get_prandom_u32];"
|
||||
"if r0 == 24 goto 2f;"
|
||||
"r1 = r10;"
|
||||
"r1 += -8;"
|
||||
"r2 = 0;"
|
||||
"r3 = 5;"
|
||||
"call %[bpf_iter_num_new];"
|
||||
"1:"
|
||||
"r1 = r10;"
|
||||
"r1 += -8;"
|
||||
"call %[bpf_iter_num_next];"
|
||||
"if r0 != 0 goto 1b;"
|
||||
"r1 = r10;"
|
||||
"r1 += -8;"
|
||||
"call %[bpf_iter_num_destroy];"
|
||||
"r0 = 0;"
|
||||
"exit;"
|
||||
"2:"
|
||||
/* loop to increase dfs_depth */
|
||||
"r0 = 10;"
|
||||
"3:"
|
||||
"r0 -= 1;"
|
||||
"if r0 != 0 goto 3b;"
|
||||
/* end of loop */
|
||||
"r1 = r10;"
|
||||
"r1 += -8;"
|
||||
"r2 = 0;"
|
||||
"r3 = 10;"
|
||||
"call %[bpf_iter_num_new];"
|
||||
"r8 = -24;"
|
||||
"main_loop_%=:"
|
||||
"r1 = r10;"
|
||||
"r1 += -8;"
|
||||
"call %[bpf_iter_num_next];"
|
||||
"if r0 == 0 goto main_loop_end_%=;"
|
||||
/* first if */
|
||||
"call %[bpf_get_prandom_u32];"
|
||||
"if r0 == 77 goto unsafe_write_%=;"
|
||||
/* second if */
|
||||
"call %[bpf_get_prandom_u32];"
|
||||
"if r0 == 42 goto poison_r8_%=;"
|
||||
/* iterate */
|
||||
"goto main_loop_%=;"
|
||||
"main_loop_end_%=:"
|
||||
"r1 = r10;"
|
||||
"r1 += -8;"
|
||||
"call %[bpf_iter_num_destroy];"
|
||||
"r0 = 0;"
|
||||
"exit;"
|
||||
|
||||
"unsafe_write_%=:"
|
||||
"r0 = r10;"
|
||||
"r0 += r8;"
|
||||
"r1 = 7;"
|
||||
"*(u64 *)(r0 + 0) = r1;"
|
||||
"goto main_loop_end_%=;"
|
||||
|
||||
"poison_r8_%=:"
|
||||
"r8 = -25;"
|
||||
"goto main_loop_%=;"
|
||||
:
|
||||
: __imm(bpf_get_prandom_u32),
|
||||
__imm(bpf_iter_num_new),
|
||||
__imm(bpf_iter_num_next),
|
||||
__imm(bpf_iter_num_destroy)
|
||||
: __clobber_all
|
||||
);
|
||||
}
|
||||
|
||||
SEC("?raw_tp")
|
||||
__success
|
||||
__naked int triple_continue(void)
|
||||
@@ -1512,4 +1630,25 @@ int iter_destroy_bad_arg(const void *ctx)
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("raw_tp")
|
||||
__success
|
||||
int clean_live_states(const void *ctx)
|
||||
{
|
||||
char buf[1];
|
||||
int i, j, k, l, m, n, o;
|
||||
|
||||
bpf_for(i, 0, 10)
|
||||
bpf_for(j, 0, 10)
|
||||
bpf_for(k, 0, 10)
|
||||
bpf_for(l, 0, 10)
|
||||
bpf_for(m, 0, 10)
|
||||
bpf_for(n, 0, 10)
|
||||
bpf_for(o, 0, 10) {
|
||||
if (unlikely(bpf_get_prandom_u32()))
|
||||
buf[0] = 42;
|
||||
bpf_printk("%s", buf);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
@@ -28,6 +28,7 @@ struct {
|
||||
} sock_map SEC(".maps");
|
||||
|
||||
int tcx_init_netns_cookie, tcx_netns_cookie;
|
||||
int cgroup_skb_init_netns_cookie, cgroup_skb_netns_cookie;
|
||||
|
||||
SEC("sockops")
|
||||
int get_netns_cookie_sockops(struct bpf_sock_ops *ctx)
|
||||
@@ -91,4 +92,12 @@ int get_netns_cookie_tcx(struct __sk_buff *skb)
|
||||
return TCX_PASS;
|
||||
}
|
||||
|
||||
SEC("cgroup_skb/ingress")
|
||||
int get_netns_cookie_cgroup_skb(struct __sk_buff *skb)
|
||||
{
|
||||
cgroup_skb_init_netns_cookie = bpf_get_netns_cookie(NULL);
|
||||
cgroup_skb_netns_cookie = bpf_get_netns_cookie(skb);
|
||||
return SK_PASS;
|
||||
}
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
@@ -134,7 +134,7 @@ int __noinline preempt_global_subprog(void)
|
||||
}
|
||||
|
||||
SEC("?tc")
|
||||
__failure __msg("global function calls are not allowed with preemption disabled")
|
||||
__success
|
||||
int preempt_global_subprog_test(struct __sk_buff *ctx)
|
||||
{
|
||||
preempt_disable();
|
||||
@@ -143,4 +143,70 @@ int preempt_global_subprog_test(struct __sk_buff *ctx)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __noinline
|
||||
global_subprog(int i)
|
||||
{
|
||||
if (i)
|
||||
bpf_printk("%p", &i);
|
||||
return i;
|
||||
}
|
||||
|
||||
int __noinline
|
||||
global_sleepable_helper_subprog(int i)
|
||||
{
|
||||
if (i)
|
||||
bpf_copy_from_user(&i, sizeof(i), NULL);
|
||||
return i;
|
||||
}
|
||||
|
||||
int __noinline
|
||||
global_sleepable_kfunc_subprog(int i)
|
||||
{
|
||||
if (i)
|
||||
bpf_copy_from_user_str(&i, sizeof(i), NULL, 0);
|
||||
global_subprog(i);
|
||||
return i;
|
||||
}
|
||||
|
||||
int __noinline
|
||||
global_subprog_calling_sleepable_global(int i)
|
||||
{
|
||||
if (!i)
|
||||
global_sleepable_kfunc_subprog(i);
|
||||
return i;
|
||||
}
|
||||
|
||||
SEC("?syscall")
|
||||
__failure __msg("global functions that may sleep are not allowed in non-sleepable context")
|
||||
int preempt_global_sleepable_helper_subprog(struct __sk_buff *ctx)
|
||||
{
|
||||
preempt_disable();
|
||||
if (ctx->mark)
|
||||
global_sleepable_helper_subprog(ctx->mark);
|
||||
preempt_enable();
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("?syscall")
|
||||
__failure __msg("global functions that may sleep are not allowed in non-sleepable context")
|
||||
int preempt_global_sleepable_kfunc_subprog(struct __sk_buff *ctx)
|
||||
{
|
||||
preempt_disable();
|
||||
if (ctx->mark)
|
||||
global_sleepable_kfunc_subprog(ctx->mark);
|
||||
preempt_enable();
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("?syscall")
|
||||
__failure __msg("global functions that may sleep are not allowed in non-sleepable context")
|
||||
int preempt_global_sleepable_subprog_indirect(struct __sk_buff *ctx)
|
||||
{
|
||||
preempt_disable();
|
||||
if (ctx->mark)
|
||||
global_subprog_calling_sleepable_global(ctx->mark);
|
||||
preempt_enable();
|
||||
return 0;
|
||||
}
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
28
tools/testing/selftests/bpf/progs/prepare.c
Normal file
28
tools/testing/selftests/bpf/progs/prepare.c
Normal file
@@ -0,0 +1,28 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2025 Meta */
|
||||
#include <vmlinux.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
//#include <bpf/bpf_tracing.h>
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
int err;
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_RINGBUF);
|
||||
__uint(max_entries, 4096);
|
||||
} ringbuf SEC(".maps");
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||
__uint(max_entries, 1);
|
||||
__type(key, __u32);
|
||||
__type(value, __u32);
|
||||
} array_map SEC(".maps");
|
||||
|
||||
SEC("cgroup_skb/egress")
|
||||
int program(struct __sk_buff *skb)
|
||||
{
|
||||
err = 0;
|
||||
return 0;
|
||||
}
|
||||
13
tools/testing/selftests/bpf/progs/priv_freplace_prog.c
Normal file
13
tools/testing/selftests/bpf/progs/priv_freplace_prog.c
Normal file
@@ -0,0 +1,13 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */
|
||||
|
||||
#include "vmlinux.h"
|
||||
#include <bpf/bpf_helpers.h>
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
SEC("freplace/xdp_prog1")
|
||||
int new_xdp_prog2(struct xdp_md *xd)
|
||||
{
|
||||
return XDP_DROP;
|
||||
}
|
||||
@@ -6,8 +6,8 @@
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
SEC("kprobe")
|
||||
int kprobe_prog(void *ctx)
|
||||
SEC("xdp")
|
||||
int xdp_prog1(struct xdp_md *xdp)
|
||||
{
|
||||
return 1;
|
||||
return XDP_DROP;
|
||||
}
|
||||
|
||||
88
tools/testing/selftests/bpf/progs/pro_epilogue_with_kfunc.c
Normal file
88
tools/testing/selftests/bpf/progs/pro_epilogue_with_kfunc.c
Normal file
@@ -0,0 +1,88 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */
|
||||
|
||||
#include <vmlinux.h>
|
||||
#include <bpf/bpf_tracing.h>
|
||||
#include "bpf_misc.h"
|
||||
#include "../test_kmods/bpf_testmod.h"
|
||||
#include "../test_kmods/bpf_testmod_kfunc.h"
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
void __kfunc_btf_root(void)
|
||||
{
|
||||
bpf_kfunc_st_ops_inc10(NULL);
|
||||
}
|
||||
|
||||
static __noinline __used int subprog(struct st_ops_args *args)
|
||||
{
|
||||
args->a += 1;
|
||||
return args->a;
|
||||
}
|
||||
|
||||
__success
|
||||
/* prologue */
|
||||
__xlated("0: r8 = r1")
|
||||
__xlated("1: r1 = 0")
|
||||
__xlated("2: call kernel-function")
|
||||
__xlated("3: if r0 != 0x0 goto pc+5")
|
||||
__xlated("4: r6 = *(u64 *)(r8 +0)")
|
||||
__xlated("5: r7 = *(u64 *)(r6 +0)")
|
||||
__xlated("6: r7 += 1000")
|
||||
__xlated("7: *(u64 *)(r6 +0) = r7")
|
||||
__xlated("8: goto pc+2")
|
||||
__xlated("9: r1 = r0")
|
||||
__xlated("10: call kernel-function")
|
||||
__xlated("11: r1 = r8")
|
||||
/* save __u64 *ctx to stack */
|
||||
__xlated("12: *(u64 *)(r10 -8) = r1")
|
||||
/* main prog */
|
||||
__xlated("13: r1 = *(u64 *)(r1 +0)")
|
||||
__xlated("14: r6 = r1")
|
||||
__xlated("15: call kernel-function")
|
||||
__xlated("16: r1 = r6")
|
||||
__xlated("17: call pc+")
|
||||
/* epilogue */
|
||||
__xlated("18: r1 = 0")
|
||||
__xlated("19: r6 = 0")
|
||||
__xlated("20: call kernel-function")
|
||||
__xlated("21: if r0 != 0x0 goto pc+6")
|
||||
__xlated("22: r1 = *(u64 *)(r10 -8)")
|
||||
__xlated("23: r1 = *(u64 *)(r1 +0)")
|
||||
__xlated("24: r6 = *(u64 *)(r1 +0)")
|
||||
__xlated("25: r6 += 10000")
|
||||
__xlated("26: *(u64 *)(r1 +0) = r6")
|
||||
__xlated("27: goto pc+2")
|
||||
__xlated("28: r1 = r0")
|
||||
__xlated("29: call kernel-function")
|
||||
__xlated("30: r0 = r6")
|
||||
__xlated("31: r0 *= 2")
|
||||
__xlated("32: exit")
|
||||
SEC("struct_ops/test_pro_epilogue")
|
||||
__naked int test_kfunc_pro_epilogue(void)
|
||||
{
|
||||
asm volatile (
|
||||
"r1 = *(u64 *)(r1 +0);"
|
||||
"r6 = r1;"
|
||||
"call %[bpf_kfunc_st_ops_inc10];"
|
||||
"r1 = r6;"
|
||||
"call subprog;"
|
||||
"exit;"
|
||||
:
|
||||
: __imm(bpf_kfunc_st_ops_inc10)
|
||||
: __clobber_all);
|
||||
}
|
||||
|
||||
SEC("syscall")
|
||||
__retval(22022) /* (PROLOGUE_A [1000] + KFUNC_INC10 + SUBPROG_A [1] + EPILOGUE_A [10000]) * 2 */
|
||||
int syscall_pro_epilogue(void *ctx)
|
||||
{
|
||||
struct st_ops_args args = {};
|
||||
|
||||
return bpf_kfunc_st_ops_test_pro_epilogue(&args);
|
||||
}
|
||||
|
||||
SEC(".struct_ops.link")
|
||||
struct bpf_testmod_st_ops pro_epilogue_with_kfunc = {
|
||||
.test_pro_epilogue = (void *)test_kfunc_pro_epilogue,
|
||||
};
|
||||
@@ -242,7 +242,8 @@ out:
|
||||
}
|
||||
|
||||
SEC("?lsm.s/bpf")
|
||||
int BPF_PROG(inproper_sleepable_kfunc, int cmd, union bpf_attr *attr, unsigned int size)
|
||||
int BPF_PROG(inproper_sleepable_kfunc, int cmd, union bpf_attr *attr, unsigned int size,
|
||||
bool kernel)
|
||||
{
|
||||
struct bpf_key *bkey;
|
||||
|
||||
@@ -439,3 +440,61 @@ int rcu_read_lock_global_subprog_unlock(void *ctx)
|
||||
ret += global_subprog_unlock(ret);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __noinline
|
||||
global_sleepable_helper_subprog(int i)
|
||||
{
|
||||
if (i)
|
||||
bpf_copy_from_user(&i, sizeof(i), NULL);
|
||||
return i;
|
||||
}
|
||||
|
||||
int __noinline
|
||||
global_sleepable_kfunc_subprog(int i)
|
||||
{
|
||||
if (i)
|
||||
bpf_copy_from_user_str(&i, sizeof(i), NULL, 0);
|
||||
global_subprog(i);
|
||||
return i;
|
||||
}
|
||||
|
||||
int __noinline
|
||||
global_subprog_calling_sleepable_global(int i)
|
||||
{
|
||||
if (!i)
|
||||
global_sleepable_kfunc_subprog(i);
|
||||
return i;
|
||||
}
|
||||
|
||||
SEC("?fentry.s/" SYS_PREFIX "sys_getpgid")
|
||||
int rcu_read_lock_sleepable_helper_global_subprog(void *ctx)
|
||||
{
|
||||
volatile int ret = 0;
|
||||
|
||||
bpf_rcu_read_lock();
|
||||
ret += global_sleepable_helper_subprog(ret);
|
||||
bpf_rcu_read_unlock();
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("?fentry.s/" SYS_PREFIX "sys_getpgid")
|
||||
int rcu_read_lock_sleepable_kfunc_global_subprog(void *ctx)
|
||||
{
|
||||
volatile int ret = 0;
|
||||
|
||||
bpf_rcu_read_lock();
|
||||
ret += global_sleepable_kfunc_subprog(ret);
|
||||
bpf_rcu_read_unlock();
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("?fentry.s/" SYS_PREFIX "sys_getpgid")
|
||||
int rcu_read_lock_sleepable_global_subprog_indirect(void *ctx)
|
||||
{
|
||||
volatile int ret = 0;
|
||||
|
||||
bpf_rcu_read_lock();
|
||||
ret += global_subprog_calling_sleepable_global(ret);
|
||||
bpf_rcu_read_unlock();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -8,14 +8,16 @@
|
||||
|
||||
int target_pid = 0;
|
||||
void *user_ptr = 0;
|
||||
int read_ret[9];
|
||||
int read_ret[10];
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
/*
|
||||
* This is the only kfunc, the others are helpers
|
||||
* These are the kfuncs, the others are helpers
|
||||
*/
|
||||
int bpf_copy_from_user_str(void *dst, u32, const void *, u64) __weak __ksym;
|
||||
int bpf_copy_from_user_task_str(void *dst, u32, const void *,
|
||||
struct task_struct *, u64) __weak __ksym;
|
||||
|
||||
SEC("fentry/" SYS_PREFIX "sys_nanosleep")
|
||||
int do_probe_read(void *ctx)
|
||||
@@ -47,6 +49,11 @@ int do_copy_from_user(void *ctx)
|
||||
read_ret[7] = bpf_copy_from_user_task(buf, sizeof(buf), user_ptr,
|
||||
bpf_get_current_task_btf(), 0);
|
||||
read_ret[8] = bpf_copy_from_user_str((char *)buf, sizeof(buf), user_ptr, 0);
|
||||
read_ret[9] = bpf_copy_from_user_task_str((char *)buf,
|
||||
sizeof(buf),
|
||||
user_ptr,
|
||||
bpf_get_current_task_btf(),
|
||||
0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
47
tools/testing/selftests/bpf/progs/set_global_vars.c
Normal file
47
tools/testing/selftests/bpf/progs/set_global_vars.c
Normal file
@@ -0,0 +1,47 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */
|
||||
#include "bpf_experimental.h"
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include "bpf_misc.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
enum Enum { EA1 = 0, EA2 = 11 };
|
||||
enum Enumu64 {EB1 = 0llu, EB2 = 12llu };
|
||||
enum Enums64 { EC1 = 0ll, EC2 = 13ll };
|
||||
|
||||
const volatile __s64 var_s64 = -1;
|
||||
const volatile __u64 var_u64 = 0;
|
||||
const volatile __s32 var_s32 = -1;
|
||||
const volatile __u32 var_u32 = 0;
|
||||
const volatile __s16 var_s16 = -1;
|
||||
const volatile __u16 var_u16 = 0;
|
||||
const volatile __s8 var_s8 = -1;
|
||||
const volatile __u8 var_u8 = 0;
|
||||
const volatile enum Enum var_ea = EA1;
|
||||
const volatile enum Enumu64 var_eb = EB1;
|
||||
const volatile enum Enums64 var_ec = EC1;
|
||||
const volatile bool var_b = false;
|
||||
|
||||
char arr[4] = {0};
|
||||
|
||||
SEC("socket")
|
||||
int test_set_globals(void *ctx)
|
||||
{
|
||||
volatile __s8 a;
|
||||
|
||||
a = var_s64;
|
||||
a = var_u64;
|
||||
a = var_s32;
|
||||
a = var_u32;
|
||||
a = var_s16;
|
||||
a = var_u16;
|
||||
a = var_s8;
|
||||
a = var_u8;
|
||||
a = var_ea;
|
||||
a = var_eb;
|
||||
a = var_ec;
|
||||
a = var_b;
|
||||
return a;
|
||||
}
|
||||
@@ -35,7 +35,10 @@ static __always_inline int local_strncmp(const char *s1, unsigned int sz,
|
||||
SEC("tp/syscalls/sys_enter_getpgid")
|
||||
int strncmp_no_helper(void *ctx)
|
||||
{
|
||||
if (local_strncmp(str, cmp_str_len + 1, target) < 0)
|
||||
const char *target_str = target;
|
||||
|
||||
barrier_var(target_str);
|
||||
if (local_strncmp(str, cmp_str_len + 1, target_str) < 0)
|
||||
__sync_add_and_fetch(&hits, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
30
tools/testing/selftests/bpf/progs/struct_ops_kptr_return.c
Normal file
30
tools/testing/selftests/bpf/progs/struct_ops_kptr_return.c
Normal file
@@ -0,0 +1,30 @@
|
||||
#include <vmlinux.h>
|
||||
#include <bpf/bpf_tracing.h>
|
||||
#include "../test_kmods/bpf_testmod.h"
|
||||
#include "bpf_misc.h"
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
void bpf_task_release(struct task_struct *p) __ksym;
|
||||
|
||||
/* This test struct_ops BPF programs returning referenced kptr. The verifier should
|
||||
* allow a referenced kptr or a NULL pointer to be returned. A referenced kptr to task
|
||||
* here is acquried automatically as the task argument is tagged with "__ref".
|
||||
*/
|
||||
SEC("struct_ops/test_return_ref_kptr")
|
||||
struct task_struct *BPF_PROG(kptr_return, int dummy,
|
||||
struct task_struct *task, struct cgroup *cgrp)
|
||||
{
|
||||
if (dummy % 2) {
|
||||
bpf_task_release(task);
|
||||
return NULL;
|
||||
}
|
||||
return task;
|
||||
}
|
||||
|
||||
SEC(".struct_ops.link")
|
||||
struct bpf_testmod_ops testmod_kptr_return = {
|
||||
.test_return_ref_kptr = (void *)kptr_return,
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
#include <vmlinux.h>
|
||||
#include <bpf/bpf_tracing.h>
|
||||
#include "../test_kmods/bpf_testmod.h"
|
||||
#include "bpf_misc.h"
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
struct cgroup *bpf_cgroup_acquire(struct cgroup *p) __ksym;
|
||||
void bpf_task_release(struct task_struct *p) __ksym;
|
||||
|
||||
/* This test struct_ops BPF programs returning referenced kptr. The verifier should
|
||||
* reject programs returning a non-zero scalar value.
|
||||
*/
|
||||
SEC("struct_ops/test_return_ref_kptr")
|
||||
__failure __msg("At program exit the register R0 has smin=1 smax=1 should have been in [0, 0]")
|
||||
struct task_struct *BPF_PROG(kptr_return_fail__invalid_scalar, int dummy,
|
||||
struct task_struct *task, struct cgroup *cgrp)
|
||||
{
|
||||
bpf_task_release(task);
|
||||
return (struct task_struct *)1;
|
||||
}
|
||||
|
||||
SEC(".struct_ops.link")
|
||||
struct bpf_testmod_ops testmod_kptr_return = {
|
||||
.test_return_ref_kptr = (void *)kptr_return_fail__invalid_scalar,
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
#include <vmlinux.h>
|
||||
#include <bpf/bpf_tracing.h>
|
||||
#include "../test_kmods/bpf_testmod.h"
|
||||
#include "bpf_experimental.h"
|
||||
#include "bpf_misc.h"
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
struct cgroup *bpf_cgroup_acquire(struct cgroup *p) __ksym;
|
||||
void bpf_task_release(struct task_struct *p) __ksym;
|
||||
|
||||
/* This test struct_ops BPF programs returning referenced kptr. The verifier should
|
||||
* reject programs returning a local kptr.
|
||||
*/
|
||||
SEC("struct_ops/test_return_ref_kptr")
|
||||
__failure __msg("At program exit the register R0 is not a known value (ptr_or_null_)")
|
||||
struct task_struct *BPF_PROG(kptr_return_fail__local_kptr, int dummy,
|
||||
struct task_struct *task, struct cgroup *cgrp)
|
||||
{
|
||||
struct task_struct *t;
|
||||
|
||||
bpf_task_release(task);
|
||||
|
||||
t = bpf_obj_new(typeof(*task));
|
||||
if (!t)
|
||||
return NULL;
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
SEC(".struct_ops.link")
|
||||
struct bpf_testmod_ops testmod_kptr_return = {
|
||||
.test_return_ref_kptr = (void *)kptr_return_fail__local_kptr,
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
#include <vmlinux.h>
|
||||
#include <bpf/bpf_tracing.h>
|
||||
#include "../test_kmods/bpf_testmod.h"
|
||||
#include "bpf_misc.h"
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
struct cgroup *bpf_cgroup_acquire(struct cgroup *p) __ksym;
|
||||
void bpf_task_release(struct task_struct *p) __ksym;
|
||||
|
||||
/* This test struct_ops BPF programs returning referenced kptr. The verifier should
|
||||
* reject programs returning a modified referenced kptr.
|
||||
*/
|
||||
SEC("struct_ops/test_return_ref_kptr")
|
||||
__failure __msg("dereference of modified trusted_ptr_ ptr R0 off={{[0-9]+}} disallowed")
|
||||
struct task_struct *BPF_PROG(kptr_return_fail__nonzero_offset, int dummy,
|
||||
struct task_struct *task, struct cgroup *cgrp)
|
||||
{
|
||||
return (struct task_struct *)&task->jobctl;
|
||||
}
|
||||
|
||||
SEC(".struct_ops.link")
|
||||
struct bpf_testmod_ops testmod_kptr_return = {
|
||||
.test_return_ref_kptr = (void *)kptr_return_fail__nonzero_offset,
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
#include <vmlinux.h>
|
||||
#include <bpf/bpf_tracing.h>
|
||||
#include "../test_kmods/bpf_testmod.h"
|
||||
#include "bpf_misc.h"
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
struct cgroup *bpf_cgroup_acquire(struct cgroup *p) __ksym;
|
||||
void bpf_task_release(struct task_struct *p) __ksym;
|
||||
|
||||
/* This test struct_ops BPF programs returning referenced kptr. The verifier should
|
||||
* reject programs returning a referenced kptr of the wrong type.
|
||||
*/
|
||||
SEC("struct_ops/test_return_ref_kptr")
|
||||
__failure __msg("At program exit the register R0 is not a known value (ptr_or_null_)")
|
||||
struct task_struct *BPF_PROG(kptr_return_fail__wrong_type, int dummy,
|
||||
struct task_struct *task, struct cgroup *cgrp)
|
||||
{
|
||||
struct task_struct *ret;
|
||||
|
||||
ret = (struct task_struct *)bpf_cgroup_acquire(cgrp);
|
||||
bpf_task_release(task);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
SEC(".struct_ops.link")
|
||||
struct bpf_testmod_ops testmod_kptr_return = {
|
||||
.test_return_ref_kptr = (void *)kptr_return_fail__wrong_type,
|
||||
};
|
||||
31
tools/testing/selftests/bpf/progs/struct_ops_refcounted.c
Normal file
31
tools/testing/selftests/bpf/progs/struct_ops_refcounted.c
Normal file
@@ -0,0 +1,31 @@
|
||||
#include <vmlinux.h>
|
||||
#include <bpf/bpf_tracing.h>
|
||||
#include "../test_kmods/bpf_testmod.h"
|
||||
#include "bpf_misc.h"
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
__attribute__((nomerge)) extern void bpf_task_release(struct task_struct *p) __ksym;
|
||||
|
||||
/* This is a test BPF program that uses struct_ops to access a referenced
|
||||
* kptr argument. This is a test for the verifier to ensure that it
|
||||
* 1) recongnizes the task as a referenced object (i.e., ref_obj_id > 0), and
|
||||
* 2) the same reference can be acquired from multiple paths as long as it
|
||||
* has not been released.
|
||||
*/
|
||||
SEC("struct_ops/test_refcounted")
|
||||
int BPF_PROG(refcounted, int dummy, struct task_struct *task)
|
||||
{
|
||||
if (dummy == 1)
|
||||
bpf_task_release(task);
|
||||
else
|
||||
bpf_task_release(task);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC(".struct_ops.link")
|
||||
struct bpf_testmod_ops testmod_refcounted = {
|
||||
.test_refcounted = (void *)refcounted,
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
#include <vmlinux.h>
|
||||
#include <bpf/bpf_tracing.h>
|
||||
#include "../test_kmods/bpf_testmod.h"
|
||||
#include "bpf_misc.h"
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
extern void bpf_task_release(struct task_struct *p) __ksym;
|
||||
|
||||
__noinline int subprog_release(__u64 *ctx __arg_ctx)
|
||||
{
|
||||
struct task_struct *task = (struct task_struct *)ctx[1];
|
||||
int dummy = (int)ctx[0];
|
||||
|
||||
bpf_task_release(task);
|
||||
|
||||
return dummy + 1;
|
||||
}
|
||||
|
||||
/* Test that the verifier rejects a program that contains a global
|
||||
* subprogram with referenced kptr arguments
|
||||
*/
|
||||
SEC("struct_ops/test_refcounted")
|
||||
__failure __log_level(2)
|
||||
__msg("Validating subprog_release() func#1...")
|
||||
__msg("invalid bpf_context access off=8. Reference may already be released")
|
||||
int refcounted_fail__global_subprog(unsigned long long *ctx)
|
||||
{
|
||||
struct task_struct *task = (struct task_struct *)ctx[1];
|
||||
|
||||
bpf_task_release(task);
|
||||
|
||||
return subprog_release(ctx);
|
||||
}
|
||||
|
||||
SEC(".struct_ops.link")
|
||||
struct bpf_testmod_ops testmod_ref_acquire = {
|
||||
.test_refcounted = (void *)refcounted_fail__global_subprog,
|
||||
};
|
||||
@@ -0,0 +1,22 @@
|
||||
#include <vmlinux.h>
|
||||
#include <bpf/bpf_tracing.h>
|
||||
#include "../test_kmods/bpf_testmod.h"
|
||||
#include "bpf_misc.h"
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
/* Test that the verifier rejects a program that acquires a referenced
|
||||
* kptr through context without releasing the reference
|
||||
*/
|
||||
SEC("struct_ops/test_refcounted")
|
||||
__failure __msg("Unreleased reference id=1 alloc_insn=0")
|
||||
int BPF_PROG(refcounted_fail__ref_leak, int dummy,
|
||||
struct task_struct *task)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC(".struct_ops.link")
|
||||
struct bpf_testmod_ops testmod_ref_acquire = {
|
||||
.test_refcounted = (void *)refcounted_fail__ref_leak,
|
||||
};
|
||||
@@ -0,0 +1,36 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */
|
||||
#include <vmlinux.h>
|
||||
#include <bpf/bpf_tracing.h>
|
||||
#include "../test_kmods/bpf_testmod.h"
|
||||
#include "bpf_misc.h"
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_PROG_ARRAY);
|
||||
__uint(max_entries, 1);
|
||||
__uint(key_size, sizeof(__u32));
|
||||
__uint(value_size, sizeof(__u32));
|
||||
} prog_array SEC(".maps");
|
||||
|
||||
/* Test that the verifier rejects a program with referenced kptr arguments
|
||||
* that tail call
|
||||
*/
|
||||
SEC("struct_ops/test_refcounted")
|
||||
__failure __msg("program with __ref argument cannot tail call")
|
||||
int refcounted_fail__tail_call(unsigned long long *ctx)
|
||||
{
|
||||
struct task_struct *task = (struct task_struct *)ctx[1];
|
||||
|
||||
bpf_task_release(task);
|
||||
bpf_tail_call(ctx, &prog_array, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC(".struct_ops.link")
|
||||
struct bpf_testmod_ops testmod_ref_acquire = {
|
||||
.test_refcounted = (void *)refcounted_fail__tail_call,
|
||||
};
|
||||
|
||||
78
tools/testing/selftests/bpf/progs/summarization.c
Normal file
78
tools/testing/selftests/bpf/progs/summarization.c
Normal file
@@ -0,0 +1,78 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include <vmlinux.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include "bpf_misc.h"
|
||||
|
||||
__noinline
|
||||
long changes_pkt_data(struct __sk_buff *sk)
|
||||
{
|
||||
return bpf_skb_pull_data(sk, 0);
|
||||
}
|
||||
|
||||
__noinline __weak
|
||||
long does_not_change_pkt_data(struct __sk_buff *sk)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("?tc")
|
||||
int main_changes_with_subprogs(struct __sk_buff *sk)
|
||||
{
|
||||
changes_pkt_data(sk);
|
||||
does_not_change_pkt_data(sk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("?tc")
|
||||
int main_changes(struct __sk_buff *sk)
|
||||
{
|
||||
bpf_skb_pull_data(sk, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("?tc")
|
||||
int main_does_not_change(struct __sk_buff *sk)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
__noinline
|
||||
long might_sleep(struct pt_regs *ctx __arg_ctx)
|
||||
{
|
||||
int i;
|
||||
|
||||
bpf_copy_from_user(&i, sizeof(i), NULL);
|
||||
return i;
|
||||
}
|
||||
|
||||
__noinline __weak
|
||||
long does_not_sleep(struct pt_regs *ctx __arg_ctx)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("?uprobe.s")
|
||||
int main_might_sleep_with_subprogs(struct pt_regs *ctx)
|
||||
{
|
||||
might_sleep(ctx);
|
||||
does_not_sleep(ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("?uprobe.s")
|
||||
int main_might_sleep(struct pt_regs *ctx)
|
||||
{
|
||||
int i;
|
||||
|
||||
bpf_copy_from_user(&i, sizeof(i), NULL);
|
||||
return i;
|
||||
}
|
||||
|
||||
SEC("?uprobe.s")
|
||||
int main_does_not_sleep(struct pt_regs *ctx)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
@@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include <linux/bpf.h>
|
||||
#include <vmlinux.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
|
||||
SEC("?freplace")
|
||||
@@ -15,4 +15,19 @@ long does_not_change_pkt_data(struct __sk_buff *sk)
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("?freplace")
|
||||
long might_sleep(struct pt_regs *ctx)
|
||||
{
|
||||
int i;
|
||||
|
||||
bpf_copy_from_user(&i, sizeof(i), NULL);
|
||||
return i;
|
||||
}
|
||||
|
||||
SEC("?freplace")
|
||||
long does_not_sleep(struct pt_regs *ctx)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
@@ -51,13 +51,13 @@ out:
|
||||
}
|
||||
|
||||
SEC("lsm/bpf")
|
||||
int BPF_PROG(lsm_run, int cmd, union bpf_attr *attr, unsigned int size)
|
||||
int BPF_PROG(lsm_run, int cmd, union bpf_attr *attr, unsigned int size, bool kernel)
|
||||
{
|
||||
return bpf_link_create_verify(cmd);
|
||||
}
|
||||
|
||||
SEC("lsm.s/bpf")
|
||||
int BPF_PROG(lsm_s_run, int cmd, union bpf_attr *attr, unsigned int size)
|
||||
int BPF_PROG(lsm_s_run, int cmd, union bpf_attr *attr, unsigned int size, bool kernel)
|
||||
{
|
||||
return bpf_link_create_verify(cmd);
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ struct {
|
||||
|
||||
struct core_reloc_arrays_output {
|
||||
int a2;
|
||||
int a3;
|
||||
char b123;
|
||||
int c1c;
|
||||
int d00d;
|
||||
@@ -41,6 +42,7 @@ int test_core_arrays(void *ctx)
|
||||
{
|
||||
struct core_reloc_arrays *in = (void *)&data.in;
|
||||
struct core_reloc_arrays_output *out = (void *)&data.out;
|
||||
int *a;
|
||||
|
||||
if (CORE_READ(&out->a2, &in->a[2]))
|
||||
return 1;
|
||||
@@ -53,6 +55,9 @@ int test_core_arrays(void *ctx)
|
||||
if (CORE_READ(&out->f01c, &in->f[0][1].c))
|
||||
return 1;
|
||||
|
||||
a = __builtin_preserve_access_index(({ in->a; }));
|
||||
out->a3 = a[0] + a[1] + a[2] + a[3];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include <bpf/bpf_tracing.h>
|
||||
#include "bpf_kfuncs.h"
|
||||
#include "bpf_misc.h"
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
@@ -17,12 +18,23 @@ static const char expected_value[] = "hello";
|
||||
char value1[32];
|
||||
char value2[32];
|
||||
|
||||
/* Matches caller of test_get_xattr() in prog_tests/fs_kfuncs.c */
|
||||
static const char xattr_names[][64] = {
|
||||
/* The following work. */
|
||||
"user.kfuncs",
|
||||
"security.bpf.xxx",
|
||||
|
||||
/* The following do not work. */
|
||||
"security.bpf",
|
||||
"security.selinux"
|
||||
};
|
||||
|
||||
SEC("lsm.s/file_open")
|
||||
int BPF_PROG(test_file_open, struct file *f)
|
||||
{
|
||||
struct bpf_dynptr value_ptr;
|
||||
__u32 pid;
|
||||
int ret;
|
||||
int ret, i;
|
||||
|
||||
pid = bpf_get_current_pid_tgid() >> 32;
|
||||
if (pid != monitored_pid)
|
||||
@@ -30,7 +42,11 @@ int BPF_PROG(test_file_open, struct file *f)
|
||||
|
||||
bpf_dynptr_from_mem(value1, sizeof(value1), 0, &value_ptr);
|
||||
|
||||
ret = bpf_get_file_xattr(f, "user.kfuncs", &value_ptr);
|
||||
for (i = 0; i < ARRAY_SIZE(xattr_names); i++) {
|
||||
ret = bpf_get_file_xattr(f, xattr_names[i], &value_ptr);
|
||||
if (ret == sizeof(expected_value))
|
||||
break;
|
||||
}
|
||||
if (ret != sizeof(expected_value))
|
||||
return 0;
|
||||
if (bpf_strncmp(value1, ret, expected_value))
|
||||
@@ -44,7 +60,7 @@ int BPF_PROG(test_inode_getxattr, struct dentry *dentry, char *name)
|
||||
{
|
||||
struct bpf_dynptr value_ptr;
|
||||
__u32 pid;
|
||||
int ret;
|
||||
int ret, i;
|
||||
|
||||
pid = bpf_get_current_pid_tgid() >> 32;
|
||||
if (pid != monitored_pid)
|
||||
@@ -52,7 +68,11 @@ int BPF_PROG(test_inode_getxattr, struct dentry *dentry, char *name)
|
||||
|
||||
bpf_dynptr_from_mem(value2, sizeof(value2), 0, &value_ptr);
|
||||
|
||||
ret = bpf_get_dentry_xattr(dentry, "user.kfuncs", &value_ptr);
|
||||
for (i = 0; i < ARRAY_SIZE(xattr_names); i++) {
|
||||
ret = bpf_get_dentry_xattr(dentry, xattr_names[i], &value_ptr);
|
||||
if (ret == sizeof(expected_value))
|
||||
break;
|
||||
}
|
||||
if (ret != sizeof(expected_value))
|
||||
return 0;
|
||||
if (bpf_strncmp(value2, ret, expected_value))
|
||||
|
||||
28
tools/testing/selftests/bpf/progs/test_kernel_flag.c
Normal file
28
tools/testing/selftests/bpf/progs/test_kernel_flag.c
Normal file
@@ -0,0 +1,28 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
/*
|
||||
* Copyright (C) 2025 Microsoft Corporation
|
||||
*
|
||||
* Author: Blaise Boscaccy <bboscaccy@linux.microsoft.com>
|
||||
*/
|
||||
|
||||
#include "vmlinux.h"
|
||||
#include <errno.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include <bpf/bpf_tracing.h>
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
__u32 monitored_tid;
|
||||
|
||||
SEC("lsm.s/bpf")
|
||||
int BPF_PROG(bpf, int cmd, union bpf_attr *attr, unsigned int size, bool kernel)
|
||||
{
|
||||
__u32 tid;
|
||||
|
||||
tid = bpf_get_current_pid_tgid() & 0xFFFFFFFF;
|
||||
if (!kernel || tid != monitored_tid)
|
||||
return 0;
|
||||
else
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -36,7 +36,7 @@ char _license[] SEC("license") = "GPL";
|
||||
|
||||
SEC("?lsm.s/bpf")
|
||||
__failure __msg("cannot pass in dynptr at an offset=-8")
|
||||
int BPF_PROG(not_valid_dynptr, int cmd, union bpf_attr *attr, unsigned int size)
|
||||
int BPF_PROG(not_valid_dynptr, int cmd, union bpf_attr *attr, unsigned int size, bool kernel)
|
||||
{
|
||||
unsigned long val;
|
||||
|
||||
@@ -46,7 +46,7 @@ int BPF_PROG(not_valid_dynptr, int cmd, union bpf_attr *attr, unsigned int size)
|
||||
|
||||
SEC("?lsm.s/bpf")
|
||||
__failure __msg("arg#0 expected pointer to stack or const struct bpf_dynptr")
|
||||
int BPF_PROG(not_ptr_to_stack, int cmd, union bpf_attr *attr, unsigned int size)
|
||||
int BPF_PROG(not_ptr_to_stack, int cmd, union bpf_attr *attr, unsigned int size, bool kernel)
|
||||
{
|
||||
unsigned long val = 0;
|
||||
|
||||
@@ -55,7 +55,7 @@ int BPF_PROG(not_ptr_to_stack, int cmd, union bpf_attr *attr, unsigned int size)
|
||||
}
|
||||
|
||||
SEC("lsm.s/bpf")
|
||||
int BPF_PROG(dynptr_data_null, int cmd, union bpf_attr *attr, unsigned int size)
|
||||
int BPF_PROG(dynptr_data_null, int cmd, union bpf_attr *attr, unsigned int size, bool kernel)
|
||||
{
|
||||
struct bpf_key *trusted_keyring;
|
||||
struct bpf_dynptr ptr;
|
||||
|
||||
@@ -23,7 +23,7 @@ extern struct bpf_key *bpf_lookup_system_key(__u64 id) __ksym;
|
||||
extern void bpf_key_put(struct bpf_key *key) __ksym;
|
||||
|
||||
SEC("lsm.s/bpf")
|
||||
int BPF_PROG(bpf, int cmd, union bpf_attr *attr, unsigned int size)
|
||||
int BPF_PROG(bpf, int cmd, union bpf_attr *attr, unsigned int size, bool kernel)
|
||||
{
|
||||
struct bpf_key *bkey;
|
||||
__u32 pid;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
char tp_name[128];
|
||||
|
||||
SEC("lsm.s/bpf")
|
||||
int BPF_PROG(lsm_run, int cmd, union bpf_attr *attr, unsigned int size)
|
||||
int BPF_PROG(lsm_run, int cmd, union bpf_attr *attr, unsigned int size, bool kernel)
|
||||
{
|
||||
switch (cmd) {
|
||||
case BPF_RAW_TRACEPOINT_OPEN:
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2018 Facebook */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/ipv6.h>
|
||||
|
||||
133
tools/testing/selftests/bpf/progs/test_set_remove_xattr.c
Normal file
133
tools/testing/selftests/bpf/progs/test_set_remove_xattr.c
Normal file
@@ -0,0 +1,133 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2024 Meta Platforms, Inc. and affiliates. */
|
||||
|
||||
#include "vmlinux.h"
|
||||
#include <errno.h>
|
||||
#include <bpf/bpf_tracing.h>
|
||||
#include "bpf_kfuncs.h"
|
||||
#include "bpf_misc.h"
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
__u32 monitored_pid;
|
||||
|
||||
const char xattr_foo[] = "security.bpf.foo";
|
||||
const char xattr_bar[] = "security.bpf.bar";
|
||||
static const char xattr_selinux[] = "security.selinux";
|
||||
char value_bar[] = "world";
|
||||
char read_value[32];
|
||||
|
||||
bool set_security_bpf_bar_success;
|
||||
bool remove_security_bpf_bar_success;
|
||||
bool set_security_selinux_fail;
|
||||
bool remove_security_selinux_fail;
|
||||
|
||||
char name_buf[32];
|
||||
|
||||
static inline bool name_match_foo(const char *name)
|
||||
{
|
||||
bpf_probe_read_kernel(name_buf, sizeof(name_buf), name);
|
||||
|
||||
return !bpf_strncmp(name_buf, sizeof(xattr_foo), xattr_foo);
|
||||
}
|
||||
|
||||
/* Test bpf_set_dentry_xattr and bpf_remove_dentry_xattr */
|
||||
SEC("lsm.s/inode_getxattr")
|
||||
int BPF_PROG(test_inode_getxattr, struct dentry *dentry, char *name)
|
||||
{
|
||||
struct bpf_dynptr value_ptr;
|
||||
__u32 pid;
|
||||
int ret;
|
||||
|
||||
pid = bpf_get_current_pid_tgid() >> 32;
|
||||
if (pid != monitored_pid)
|
||||
return 0;
|
||||
|
||||
/* Only do the following for security.bpf.foo */
|
||||
if (!name_match_foo(name))
|
||||
return 0;
|
||||
|
||||
bpf_dynptr_from_mem(read_value, sizeof(read_value), 0, &value_ptr);
|
||||
|
||||
/* read security.bpf.bar */
|
||||
ret = bpf_get_dentry_xattr(dentry, xattr_bar, &value_ptr);
|
||||
|
||||
if (ret < 0) {
|
||||
/* If security.bpf.bar doesn't exist, set it */
|
||||
bpf_dynptr_from_mem(value_bar, sizeof(value_bar), 0, &value_ptr);
|
||||
|
||||
ret = bpf_set_dentry_xattr(dentry, xattr_bar, &value_ptr, 0);
|
||||
if (!ret)
|
||||
set_security_bpf_bar_success = true;
|
||||
ret = bpf_set_dentry_xattr(dentry, xattr_selinux, &value_ptr, 0);
|
||||
if (ret)
|
||||
set_security_selinux_fail = true;
|
||||
} else {
|
||||
/* If security.bpf.bar exists, remove it */
|
||||
ret = bpf_remove_dentry_xattr(dentry, xattr_bar);
|
||||
if (!ret)
|
||||
remove_security_bpf_bar_success = true;
|
||||
|
||||
ret = bpf_remove_dentry_xattr(dentry, xattr_selinux);
|
||||
if (ret)
|
||||
remove_security_selinux_fail = true;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool locked_set_security_bpf_bar_success;
|
||||
bool locked_remove_security_bpf_bar_success;
|
||||
bool locked_set_security_selinux_fail;
|
||||
bool locked_remove_security_selinux_fail;
|
||||
|
||||
/* Test bpf_set_dentry_xattr_locked and bpf_remove_dentry_xattr_locked.
|
||||
* It not necessary to differentiate the _locked version and the
|
||||
* not-_locked version in the BPF program. The verifier will fix them up
|
||||
* properly.
|
||||
*/
|
||||
SEC("lsm.s/inode_setxattr")
|
||||
int BPF_PROG(test_inode_setxattr, struct mnt_idmap *idmap,
|
||||
struct dentry *dentry, const char *name,
|
||||
const void *value, size_t size, int flags)
|
||||
{
|
||||
struct bpf_dynptr value_ptr;
|
||||
__u32 pid;
|
||||
int ret;
|
||||
|
||||
pid = bpf_get_current_pid_tgid() >> 32;
|
||||
if (pid != monitored_pid)
|
||||
return 0;
|
||||
|
||||
/* Only do the following for security.bpf.foo */
|
||||
if (!name_match_foo(name))
|
||||
return 0;
|
||||
|
||||
bpf_dynptr_from_mem(read_value, sizeof(read_value), 0, &value_ptr);
|
||||
|
||||
/* read security.bpf.bar */
|
||||
ret = bpf_get_dentry_xattr(dentry, xattr_bar, &value_ptr);
|
||||
|
||||
if (ret < 0) {
|
||||
/* If security.bpf.bar doesn't exist, set it */
|
||||
bpf_dynptr_from_mem(value_bar, sizeof(value_bar), 0, &value_ptr);
|
||||
|
||||
ret = bpf_set_dentry_xattr(dentry, xattr_bar, &value_ptr, 0);
|
||||
if (!ret)
|
||||
locked_set_security_bpf_bar_success = true;
|
||||
ret = bpf_set_dentry_xattr(dentry, xattr_selinux, &value_ptr, 0);
|
||||
if (ret)
|
||||
locked_set_security_selinux_fail = true;
|
||||
} else {
|
||||
/* If security.bpf.bar exists, remove it */
|
||||
ret = bpf_remove_dentry_xattr(dentry, xattr_bar);
|
||||
if (!ret)
|
||||
locked_remove_security_bpf_bar_success = true;
|
||||
|
||||
ret = bpf_remove_dentry_xattr(dentry, xattr_selinux);
|
||||
if (ret)
|
||||
locked_remove_security_selinux_fail = true;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user