Skip to content
Permalink
Browse files
net/netfilter: Add unstable CT lookup helpers for XDP and TC-BPF
This change adds conntrack lookup helpers using the unstable kfunc call
interface for the XDP and TC-BPF hooks. The primary usecase is
implementing a synproxy in XDP, see Maxim's patchset at [0].

Also add acquire/release functions (randomly returning NULL), and also
exercise the PTR_TO_BTF_ID_OR_NULL path so that BPF program caller has
to check for NULL before dereferencing the pointer, for the TC hook.
Introduce kfunc that take various argument types (for PTR_TO_MEM) that
will pass and fail the verifier checks. These will be used in selftests.

Export get_net_ns_by_id as nf_conntrack needs to call it.

Note that we search for acquire, release, and null returning kfuncs in
the intersection of those sets and main set.

This implies that the kfunc_btf_id_list acq_set, rel_set, null_set may
contain BTF ID not in main set, this is explicitly allowed and
recommended (to save on definining more and more sets), since
check_kfunc_call verifier operation would filter out the invalid BTF ID
fairly early, so later checks for acquire, release, and ret_type_null
kfunc will only consider allowed BTF IDs for that program that are
allowed in main set. This is why the nf_conntrack_acq_ids set has BTF
IDs for both xdp and tc hook kfuncs.

  [0]: https://lore.kernel.org/bpf/20211019144655.3483197-1-maximmi@nvidia.com

Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
  • Loading branch information
kkdwivedi authored and intel-lab-lkp committed Dec 10, 2021
1 parent 59d53f1 commit 683d78cc594f7867b8dae78b357ab82a5ee69484
Show file tree
Hide file tree
Showing 7 changed files with 497 additions and 1 deletion.
@@ -1675,6 +1675,9 @@ int bpf_prog_test_run_sk_lookup(struct bpf_prog *prog,
const union bpf_attr *kattr,
union bpf_attr __user *uattr);
bool bpf_prog_test_check_kfunc_call(u32 kfunc_id, struct module *owner);
bool bpf_prog_test_is_acquire_kfunc(u32 kfunc_id, struct module *owner);
bool bpf_prog_test_is_release_kfunc(u32 kfunc_id, struct module *owner);
bool bpf_prog_test_is_kfunc_ret_type_null(u32 kfunc_id, struct module *owner);
bool btf_ctx_access(int off, int size, enum bpf_access_type type,
const struct bpf_prog *prog,
struct bpf_insn_access_aux *info);
@@ -1933,6 +1936,24 @@ static inline bool bpf_prog_test_check_kfunc_call(u32 kfunc_id,
return false;
}

static inline bool bpf_prog_test_is_acquire_kfunc(u32 kfunc_id,
struct module *owner)
{
return false;
}

static inline bool bpf_prog_test_is_release_kfunc(u32 kfunc_id,
struct module *owner)
{
return false;
}

static inline bool bpf_prog_test_is_kfunc_ret_type_null(u32 kfunc_id,
struct module *owner)
{
return false;
}

static inline void bpf_map_put(struct bpf_map *map)
{
}
@@ -321,7 +321,10 @@ static inline const char *btf_name_by_offset(const struct btf *btf,
#endif

enum kfunc_btf_id_set_types {
BTF_SET_CHECK,
BTF_SET_CHECK, /* Allowed kfunc set */
BTF_SET_ACQUIRE, /* Acquire kfunc set */
BTF_SET_RELEASE, /* Release kfunc set */
BTF_SET_RET_NULL, /* kfunc with 'return type PTR_TO_BTF_ID_OR_NULL' set */
__BTF_SET_MAX,
};

@@ -331,6 +334,9 @@ struct kfunc_btf_id_set {
struct btf_id_set *sets[__BTF_SET_MAX];
struct {
struct btf_id_set *set;
struct btf_id_set *acq_set;
struct btf_id_set *rel_set;
struct btf_id_set *null_set;
};
};
struct module *owner;
@@ -345,6 +351,12 @@ void unregister_kfunc_btf_id_set(struct kfunc_btf_id_list *l,
struct kfunc_btf_id_set *s);
bool bpf_check_mod_kfunc_call(struct kfunc_btf_id_list *klist, u32 kfunc_id,
struct module *owner);
bool bpf_is_mod_acquire_kfunc(struct kfunc_btf_id_list *klist, u32 kfunc_id,
struct module *owner);
bool bpf_is_mod_release_kfunc(struct kfunc_btf_id_list *klist, u32 kfunc_id,
struct module *owner);
bool bpf_is_mod_kfunc_ret_type_null(struct kfunc_btf_id_list *klist,
u32 kfunc_id, struct module *owner);
#else
static inline void register_kfunc_btf_id_set(struct kfunc_btf_id_list *l,
struct kfunc_btf_id_set *s)
@@ -359,9 +371,26 @@ static inline bool bpf_check_mod_kfunc_call(struct kfunc_btf_id_list *klist,
{
return false;
}
static inline bool bpf_is_mod_acquire_kfunc(struct kfunc_btf_id_list *klist,
u32 kfunc_id, struct module *owner)
{
return false;
}
static inline bool bpf_is_mod_release_kfunc(struct kfunc_btf_id_list *klist,
u32 kfunc_id, struct module *owner)
{
return false;
}
static inline bool
bpf_is_mod_kfunc_ret_type_null(struct kfunc_btf_id_list *klist, u32 kfunc_id,
struct module *owner)
{
return false;
}
#endif

extern struct kfunc_btf_id_list bpf_tcp_ca_kfunc_list;
extern struct kfunc_btf_id_list prog_test_kfunc_list;
extern struct kfunc_btf_id_list xdp_kfunc_list;

#endif
@@ -6539,6 +6539,24 @@ bool bpf_check_mod_kfunc_call(struct kfunc_btf_id_list *klist, u32 kfunc_id,
return kfunc_btf_id_set_contains(klist, kfunc_id, owner, BTF_SET_CHECK);
}

bool bpf_is_mod_acquire_kfunc(struct kfunc_btf_id_list *klist, u32 kfunc_id,
struct module *owner)
{
return kfunc_btf_id_set_contains(klist, kfunc_id, owner, BTF_SET_ACQUIRE);
}

bool bpf_is_mod_release_kfunc(struct kfunc_btf_id_list *klist, u32 kfunc_id,
struct module *owner)
{
return kfunc_btf_id_set_contains(klist, kfunc_id, owner, BTF_SET_RELEASE);
}

bool bpf_is_mod_kfunc_ret_type_null(struct kfunc_btf_id_list *klist,
u32 kfunc_id, struct module *owner)
{
return kfunc_btf_id_set_contains(klist, kfunc_id, owner, BTF_SET_RET_NULL);
}

#endif

#define DEFINE_KFUNC_BTF_ID_LIST(name) \
@@ -6548,6 +6566,7 @@ bool bpf_check_mod_kfunc_call(struct kfunc_btf_id_list *klist, u32 kfunc_id,

DEFINE_KFUNC_BTF_ID_LIST(bpf_tcp_ca_kfunc_list);
DEFINE_KFUNC_BTF_ID_LIST(prog_test_kfunc_list);
DEFINE_KFUNC_BTF_ID_LIST(xdp_kfunc_list);

int bpf_core_types_are_compat(const struct btf *local_btf, __u32 local_id,
const struct btf *targ_btf, __u32 targ_id)
@@ -232,6 +232,117 @@ struct sock * noinline bpf_kfunc_call_test3(struct sock *sk)
return sk;
}

struct prog_test_ref_kfunc {
int a;
int b;
struct prog_test_ref_kfunc *next;
};

static struct prog_test_ref_kfunc prog_test_struct = {
.a = 42,
.b = 108,
.next = &prog_test_struct,
};

noinline struct prog_test_ref_kfunc *
bpf_kfunc_call_test_acquire(unsigned long *scalar_ptr)
{
/* randomly return NULL */
if (get_jiffies_64() % 2)
return NULL;
return &prog_test_struct;
}

noinline void bpf_kfunc_call_test_release(struct prog_test_ref_kfunc *p)
{
}

struct prog_test_pass1 {
int x0;
struct {
int x1;
struct {
int x2;
struct {
int x3;
struct {
int x4;
struct {
int x5;
struct {
int x6;
struct {
int x7;
};
};
};
};
};
};
};
};

struct prog_test_pass2 {
int len;
short arr1[4];
struct {
char arr2[4];
unsigned long arr3[8];
} x;
};

struct prog_test_fail1 {
void *p;
int x;
};

struct prog_test_fail2 {
int x8;
struct prog_test_pass1 x;
};

struct prog_test_fail3 {
int len;
char arr1[2];
char arr2[0];
};

noinline void bpf_kfunc_call_test_pass_ctx(struct __sk_buff *skb)
{
}

noinline void bpf_kfunc_call_test_pass1(struct prog_test_pass1 *p)
{
}

noinline void bpf_kfunc_call_test_pass2(struct prog_test_pass2 *p)
{
}

noinline void bpf_kfunc_call_test_fail1(struct prog_test_fail1 *p)
{
}

noinline void bpf_kfunc_call_test_fail2(struct prog_test_fail2 *p)
{
}

noinline void bpf_kfunc_call_test_fail3(struct prog_test_fail3 *p)
{
}

noinline void bpf_kfunc_call_test_mem_len_pass1(void *mem, int len__mem)
{
}

noinline void bpf_kfunc_call_test_mem_len_fail1(void *mem, int len)
{
}

noinline void bpf_kfunc_call_test_mem_len_fail2(u64 *mem, int len)
{
}

__diag_pop();

ALLOW_ERROR_INJECTION(bpf_modify_return_test, ERRNO);
@@ -240,15 +351,51 @@ BTF_SET_START(test_sk_kfunc_ids)
BTF_ID(func, bpf_kfunc_call_test1)
BTF_ID(func, bpf_kfunc_call_test2)
BTF_ID(func, bpf_kfunc_call_test3)
BTF_ID(func, bpf_kfunc_call_test_acquire)
BTF_ID(func, bpf_kfunc_call_test_release)
BTF_ID(func, bpf_kfunc_call_test_pass_ctx)
BTF_ID(func, bpf_kfunc_call_test_pass1)
BTF_ID(func, bpf_kfunc_call_test_pass2)
BTF_ID(func, bpf_kfunc_call_test_fail1)
BTF_ID(func, bpf_kfunc_call_test_fail2)
BTF_ID(func, bpf_kfunc_call_test_fail3)
BTF_ID(func, bpf_kfunc_call_test_mem_len_pass1)
BTF_ID(func, bpf_kfunc_call_test_mem_len_fail1)
BTF_ID(func, bpf_kfunc_call_test_mem_len_fail2)
BTF_SET_END(test_sk_kfunc_ids)

BTF_ID_LIST(test_sk_acq_rel)
BTF_ID(func, bpf_kfunc_call_test_acquire)
BTF_ID(func, bpf_kfunc_call_test_release)

bool bpf_prog_test_check_kfunc_call(u32 kfunc_id, struct module *owner)
{
if (btf_id_set_contains(&test_sk_kfunc_ids, kfunc_id))
return true;
return bpf_check_mod_kfunc_call(&prog_test_kfunc_list, kfunc_id, owner);
}

bool bpf_prog_test_is_acquire_kfunc(u32 kfunc_id, struct module *owner)
{
if (kfunc_id == test_sk_acq_rel[0])
return true;
return bpf_is_mod_acquire_kfunc(&prog_test_kfunc_list, kfunc_id, owner);
}

bool bpf_prog_test_is_release_kfunc(u32 kfunc_id, struct module *owner)
{
if (kfunc_id == test_sk_acq_rel[1])
return true;
return bpf_is_mod_release_kfunc(&prog_test_kfunc_list, kfunc_id, owner);
}

bool bpf_prog_test_is_kfunc_ret_type_null(u32 kfunc_id, struct module *owner)
{
if (kfunc_id == test_sk_acq_rel[0])
return true;
return bpf_is_mod_kfunc_ret_type_null(&prog_test_kfunc_list, kfunc_id, owner);
}

static void *bpf_test_init(const union bpf_attr *kattr, u32 size,
u32 headroom, u32 tailroom)
{
@@ -10000,17 +10000,44 @@ const struct bpf_verifier_ops tc_cls_act_verifier_ops = {
.gen_prologue = tc_cls_act_prologue,
.gen_ld_abs = bpf_gen_ld_abs,
.check_kfunc_call = bpf_prog_test_check_kfunc_call,
.is_acquire_kfunc = bpf_prog_test_is_acquire_kfunc,
.is_release_kfunc = bpf_prog_test_is_release_kfunc,
.is_kfunc_ret_type_null = bpf_prog_test_is_kfunc_ret_type_null,
};

const struct bpf_prog_ops tc_cls_act_prog_ops = {
.test_run = bpf_prog_test_run_skb,
};

static bool xdp_check_kfunc_call(u32 kfunc_id, struct module *owner)
{
return bpf_check_mod_kfunc_call(&xdp_kfunc_list, kfunc_id, owner);
}

static bool xdp_is_acquire_kfunc(u32 kfunc_id, struct module *owner)
{
return bpf_is_mod_acquire_kfunc(&xdp_kfunc_list, kfunc_id, owner);
}

static bool xdp_is_release_kfunc(u32 kfunc_id, struct module *owner)
{
return bpf_is_mod_release_kfunc(&xdp_kfunc_list, kfunc_id, owner);
}

static bool xdp_is_kfunc_ret_type_null(u32 kfunc_id, struct module *owner)
{
return bpf_is_mod_kfunc_ret_type_null(&xdp_kfunc_list, kfunc_id, owner);
}

const struct bpf_verifier_ops xdp_verifier_ops = {
.get_func_proto = xdp_func_proto,
.is_valid_access = xdp_is_valid_access,
.convert_ctx_access = xdp_convert_ctx_access,
.gen_prologue = bpf_noop_prologue,
.check_kfunc_call = xdp_check_kfunc_call,
.is_acquire_kfunc = xdp_is_acquire_kfunc,
.is_release_kfunc = xdp_is_release_kfunc,
.is_kfunc_ret_type_null = xdp_is_kfunc_ret_type_null,
};

const struct bpf_prog_ops xdp_prog_ops = {
@@ -299,6 +299,7 @@ struct net *get_net_ns_by_id(const struct net *net, int id)

return peer;
}
EXPORT_SYMBOL_GPL(get_net_ns_by_id);

/*
* setup_net runs the initializers for the network namespace object.

0 comments on commit 683d78c

Please sign in to comment.