mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2024-12-29 09:12:07 +00:00
bpf: Add bpf_skc_to_tcp6_sock() helper
The helper is used in tracing programs to cast a socket pointer to a tcp6_sock pointer. The return value could be NULL if the casting is illegal. A new helper return type RET_PTR_TO_BTF_ID_OR_NULL is added so the verifier is able to deduce proper return types for the helper. Different from the previous BTF_ID based helpers, the bpf_skc_to_tcp6_sock() argument can be several possible btf_ids. More specifically, all possible socket data structures with sock_common appearing in the first in the memory layout. This patch only added socket types related to tcp and udp. All possible argument btf_id and return value btf_id for helper bpf_skc_to_tcp6_sock() are pre-calculcated and cached. In the future, it is even possible to precompute these btf_id's at kernel build time. Signed-off-by: Yonghong Song <yhs@fb.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Acked-by: Andrii Nakryiko <andriin@fb.com> Acked-by: Martin KaFai Lau <kafai@fb.com> Link: https://lore.kernel.org/bpf/20200623230809.3988195-1-yhs@fb.com
This commit is contained in:
parent
72e2b2b66f
commit
af7ec13833
@ -265,6 +265,7 @@ enum bpf_return_type {
|
||||
RET_PTR_TO_TCP_SOCK_OR_NULL, /* returns a pointer to a tcp_sock or NULL */
|
||||
RET_PTR_TO_SOCK_COMMON_OR_NULL, /* returns a pointer to a sock_common or NULL */
|
||||
RET_PTR_TO_ALLOC_MEM_OR_NULL, /* returns a pointer to dynamically allocated memory or NULL */
|
||||
RET_PTR_TO_BTF_ID_OR_NULL, /* returns a pointer to a btf_id or NULL */
|
||||
};
|
||||
|
||||
/* eBPF function prototype used by verifier to allow BPF_CALLs from eBPF programs
|
||||
@ -287,6 +288,12 @@ struct bpf_func_proto {
|
||||
enum bpf_arg_type arg_type[5];
|
||||
};
|
||||
int *btf_id; /* BTF ids of arguments */
|
||||
bool (*check_btf_id)(u32 btf_id, u32 arg); /* if the argument btf_id is
|
||||
* valid. Often used if more
|
||||
* than one btf id is permitted
|
||||
* for this argument.
|
||||
*/
|
||||
int *ret_btf_id; /* return value btf_id */
|
||||
};
|
||||
|
||||
/* bpf_context is intentionally undefined structure. Pointer to bpf_context is
|
||||
@ -1524,6 +1531,7 @@ static inline bool bpf_map_is_dev_bound(struct bpf_map *map)
|
||||
|
||||
struct bpf_map *bpf_map_offload_map_alloc(union bpf_attr *attr);
|
||||
void bpf_map_offload_map_free(struct bpf_map *map);
|
||||
void init_btf_sock_ids(struct btf *btf);
|
||||
#else
|
||||
static inline int bpf_prog_offload_init(struct bpf_prog *prog,
|
||||
union bpf_attr *attr)
|
||||
@ -1549,6 +1557,9 @@ static inline struct bpf_map *bpf_map_offload_map_alloc(union bpf_attr *attr)
|
||||
static inline void bpf_map_offload_map_free(struct bpf_map *map)
|
||||
{
|
||||
}
|
||||
static inline void init_btf_sock_ids(struct btf *btf)
|
||||
{
|
||||
}
|
||||
#endif /* CONFIG_NET && CONFIG_BPF_SYSCALL */
|
||||
|
||||
#if defined(CONFIG_BPF_STREAM_PARSER)
|
||||
@ -1638,6 +1649,7 @@ extern const struct bpf_func_proto bpf_ringbuf_reserve_proto;
|
||||
extern const struct bpf_func_proto bpf_ringbuf_submit_proto;
|
||||
extern const struct bpf_func_proto bpf_ringbuf_discard_proto;
|
||||
extern const struct bpf_func_proto bpf_ringbuf_query_proto;
|
||||
extern const struct bpf_func_proto bpf_skc_to_tcp6_sock_proto;
|
||||
|
||||
const struct bpf_func_proto *bpf_tracing_func_proto(
|
||||
enum bpf_func_id func_id, const struct bpf_prog *prog);
|
||||
|
@ -3255,6 +3255,12 @@ union bpf_attr {
|
||||
* case of **BPF_CSUM_LEVEL_QUERY**, the current skb->csum_level
|
||||
* is returned or the error code -EACCES in case the skb is not
|
||||
* subject to CHECKSUM_UNNECESSARY.
|
||||
*
|
||||
* struct tcp6_sock *bpf_skc_to_tcp6_sock(void *sk)
|
||||
* Description
|
||||
* Dynamically cast a *sk* pointer to a *tcp6_sock* pointer.
|
||||
* Return
|
||||
* *sk* if casting is valid, or NULL otherwise.
|
||||
*/
|
||||
#define __BPF_FUNC_MAPPER(FN) \
|
||||
FN(unspec), \
|
||||
@ -3392,7 +3398,8 @@ union bpf_attr {
|
||||
FN(ringbuf_submit), \
|
||||
FN(ringbuf_discard), \
|
||||
FN(ringbuf_query), \
|
||||
FN(csum_level),
|
||||
FN(csum_level), \
|
||||
FN(skc_to_tcp6_sock),
|
||||
|
||||
/* integer value in 'imm' field of BPF_CALL instruction selects which helper
|
||||
* function eBPF program intends to call
|
||||
|
@ -3674,6 +3674,7 @@ struct btf *btf_parse_vmlinux(void)
|
||||
goto errout;
|
||||
|
||||
bpf_struct_ops_init(btf, log);
|
||||
init_btf_sock_ids(btf);
|
||||
|
||||
btf_verifier_env_free(env);
|
||||
refcount_set(&btf->refcnt, 1);
|
||||
|
@ -3800,12 +3800,14 @@ static int int_ptr_type_to_size(enum bpf_arg_type type)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int check_func_arg(struct bpf_verifier_env *env, u32 regno,
|
||||
enum bpf_arg_type arg_type,
|
||||
struct bpf_call_arg_meta *meta)
|
||||
static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
|
||||
struct bpf_call_arg_meta *meta,
|
||||
const struct bpf_func_proto *fn)
|
||||
{
|
||||
u32 regno = BPF_REG_1 + arg;
|
||||
struct bpf_reg_state *regs = cur_regs(env), *reg = ®s[regno];
|
||||
enum bpf_reg_type expected_type, type = reg->type;
|
||||
enum bpf_arg_type arg_type = fn->arg_type[arg];
|
||||
int err = 0;
|
||||
|
||||
if (arg_type == ARG_DONTCARE)
|
||||
@ -3885,9 +3887,16 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 regno,
|
||||
expected_type = PTR_TO_BTF_ID;
|
||||
if (type != expected_type)
|
||||
goto err_type;
|
||||
if (reg->btf_id != meta->btf_id) {
|
||||
verbose(env, "Helper has type %s got %s in R%d\n",
|
||||
kernel_type_name(meta->btf_id),
|
||||
if (!fn->check_btf_id) {
|
||||
if (reg->btf_id != meta->btf_id) {
|
||||
verbose(env, "Helper has type %s got %s in R%d\n",
|
||||
kernel_type_name(meta->btf_id),
|
||||
kernel_type_name(reg->btf_id), regno);
|
||||
|
||||
return -EACCES;
|
||||
}
|
||||
} else if (!fn->check_btf_id(reg->btf_id, arg)) {
|
||||
verbose(env, "Helper does not support %s in R%d\n",
|
||||
kernel_type_name(reg->btf_id), regno);
|
||||
|
||||
return -EACCES;
|
||||
@ -4709,10 +4718,12 @@ static int check_helper_call(struct bpf_verifier_env *env, int func_id, int insn
|
||||
meta.func_id = func_id;
|
||||
/* check args */
|
||||
for (i = 0; i < 5; i++) {
|
||||
err = btf_resolve_helper_id(&env->log, fn, i);
|
||||
if (err > 0)
|
||||
meta.btf_id = err;
|
||||
err = check_func_arg(env, BPF_REG_1 + i, fn->arg_type[i], &meta);
|
||||
if (!fn->check_btf_id) {
|
||||
err = btf_resolve_helper_id(&env->log, fn, i);
|
||||
if (err > 0)
|
||||
meta.btf_id = err;
|
||||
}
|
||||
err = check_func_arg(env, i, &meta, fn);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
@ -4815,6 +4826,18 @@ static int check_helper_call(struct bpf_verifier_env *env, int func_id, int insn
|
||||
regs[BPF_REG_0].type = PTR_TO_MEM_OR_NULL;
|
||||
regs[BPF_REG_0].id = ++env->id_gen;
|
||||
regs[BPF_REG_0].mem_size = meta.mem_size;
|
||||
} else if (fn->ret_type == RET_PTR_TO_BTF_ID_OR_NULL) {
|
||||
int ret_btf_id;
|
||||
|
||||
mark_reg_known_zero(env, regs, BPF_REG_0);
|
||||
regs[BPF_REG_0].type = PTR_TO_BTF_ID_OR_NULL;
|
||||
ret_btf_id = *fn->ret_btf_id;
|
||||
if (ret_btf_id == 0) {
|
||||
verbose(env, "invalid return type %d of func %s#%d\n",
|
||||
fn->ret_type, func_id_name(func_id), func_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
regs[BPF_REG_0].btf_id = ret_btf_id;
|
||||
} else {
|
||||
verbose(env, "unknown return type %d of func %s#%d\n",
|
||||
fn->ret_type, func_id_name(func_id), func_id);
|
||||
|
@ -1515,6 +1515,8 @@ tracing_prog_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
|
||||
return &bpf_skb_output_proto;
|
||||
case BPF_FUNC_xdp_output:
|
||||
return &bpf_xdp_output_proto;
|
||||
case BPF_FUNC_skc_to_tcp6_sock:
|
||||
return &bpf_skc_to_tcp6_sock_proto;
|
||||
#endif
|
||||
case BPF_FUNC_seq_printf:
|
||||
return prog->expected_attach_type == BPF_TRACE_ITER ?
|
||||
|
@ -47,6 +47,7 @@
|
||||
#include <linux/seccomp.h>
|
||||
#include <linux/if_vlan.h>
|
||||
#include <linux/bpf.h>
|
||||
#include <linux/btf.h>
|
||||
#include <net/sch_generic.h>
|
||||
#include <net/cls_cgroup.h>
|
||||
#include <net/dst_metadata.h>
|
||||
@ -9225,3 +9226,84 @@ void bpf_prog_change_xdp(struct bpf_prog *prev_prog, struct bpf_prog *prog)
|
||||
{
|
||||
bpf_dispatcher_change_prog(BPF_DISPATCHER_PTR(xdp), prev_prog, prog);
|
||||
}
|
||||
|
||||
/* Define a list of socket types which can be the argument for
|
||||
* skc_to_*_sock() helpers. All these sockets should have
|
||||
* sock_common as the first argument in its memory layout.
|
||||
*/
|
||||
#define BTF_SOCK_TYPE_xxx \
|
||||
BTF_SOCK_TYPE(BTF_SOCK_TYPE_INET, "inet_sock") \
|
||||
BTF_SOCK_TYPE(BTF_SOCK_TYPE_INET_CONN, "inet_connection_sock") \
|
||||
BTF_SOCK_TYPE(BTF_SOCK_TYPE_INET_REQ, "inet_request_sock") \
|
||||
BTF_SOCK_TYPE(BTF_SOCK_TYPE_INET_TW, "inet_timewait_sock") \
|
||||
BTF_SOCK_TYPE(BTF_SOCK_TYPE_REQ, "request_sock") \
|
||||
BTF_SOCK_TYPE(BTF_SOCK_TYPE_SOCK, "sock") \
|
||||
BTF_SOCK_TYPE(BTF_SOCK_TYPE_SOCK_COMMON, "sock_common") \
|
||||
BTF_SOCK_TYPE(BTF_SOCK_TYPE_TCP, "tcp_sock") \
|
||||
BTF_SOCK_TYPE(BTF_SOCK_TYPE_TCP_REQ, "tcp_request_sock") \
|
||||
BTF_SOCK_TYPE(BTF_SOCK_TYPE_TCP_TW, "tcp_timewait_sock") \
|
||||
BTF_SOCK_TYPE(BTF_SOCK_TYPE_TCP6, "tcp6_sock") \
|
||||
BTF_SOCK_TYPE(BTF_SOCK_TYPE_UDP, "udp_sock") \
|
||||
BTF_SOCK_TYPE(BTF_SOCK_TYPE_UDP6, "udp6_sock")
|
||||
|
||||
enum {
|
||||
#define BTF_SOCK_TYPE(name, str) name,
|
||||
BTF_SOCK_TYPE_xxx
|
||||
#undef BTF_SOCK_TYPE
|
||||
MAX_BTF_SOCK_TYPE,
|
||||
};
|
||||
|
||||
static int btf_sock_ids[MAX_BTF_SOCK_TYPE];
|
||||
|
||||
#ifdef CONFIG_BPF_SYSCALL
|
||||
static const char *bpf_sock_types[] = {
|
||||
#define BTF_SOCK_TYPE(name, str) str,
|
||||
BTF_SOCK_TYPE_xxx
|
||||
#undef BTF_SOCK_TYPE
|
||||
};
|
||||
|
||||
void init_btf_sock_ids(struct btf *btf)
|
||||
{
|
||||
int i, btf_id;
|
||||
|
||||
for (i = 0; i < MAX_BTF_SOCK_TYPE; i++) {
|
||||
btf_id = btf_find_by_name_kind(btf, bpf_sock_types[i],
|
||||
BTF_KIND_STRUCT);
|
||||
if (btf_id > 0)
|
||||
btf_sock_ids[i] = btf_id;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool check_arg_btf_id(u32 btf_id, u32 arg)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* only one argument, no need to check arg */
|
||||
for (i = 0; i < MAX_BTF_SOCK_TYPE; i++)
|
||||
if (btf_sock_ids[i] == btf_id)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
BPF_CALL_1(bpf_skc_to_tcp6_sock, struct sock *, sk)
|
||||
{
|
||||
/* tcp6_sock type is not generated in dwarf and hence btf,
|
||||
* trigger an explicit type generation here.
|
||||
*/
|
||||
BTF_TYPE_EMIT(struct tcp6_sock);
|
||||
if (sk_fullsock(sk) && sk->sk_protocol == IPPROTO_TCP &&
|
||||
sk->sk_family == AF_INET6)
|
||||
return (unsigned long)sk;
|
||||
|
||||
return (unsigned long)NULL;
|
||||
}
|
||||
|
||||
const struct bpf_func_proto bpf_skc_to_tcp6_sock_proto = {
|
||||
.func = bpf_skc_to_tcp6_sock,
|
||||
.gpl_only = false,
|
||||
.ret_type = RET_PTR_TO_BTF_ID_OR_NULL,
|
||||
.arg1_type = ARG_PTR_TO_BTF_ID,
|
||||
.check_btf_id = check_arg_btf_id,
|
||||
.ret_btf_id = &btf_sock_ids[BTF_SOCK_TYPE_TCP6],
|
||||
};
|
||||
|
@ -421,6 +421,7 @@ class PrinterHelpers(Printer):
|
||||
'struct sockaddr',
|
||||
'struct tcphdr',
|
||||
'struct seq_file',
|
||||
'struct tcp6_sock',
|
||||
|
||||
'struct __sk_buff',
|
||||
'struct sk_msg_md',
|
||||
@ -458,6 +459,7 @@ class PrinterHelpers(Printer):
|
||||
'struct sockaddr',
|
||||
'struct tcphdr',
|
||||
'struct seq_file',
|
||||
'struct tcp6_sock',
|
||||
}
|
||||
mapped_types = {
|
||||
'u8': '__u8',
|
||||
|
@ -3255,6 +3255,12 @@ union bpf_attr {
|
||||
* case of **BPF_CSUM_LEVEL_QUERY**, the current skb->csum_level
|
||||
* is returned or the error code -EACCES in case the skb is not
|
||||
* subject to CHECKSUM_UNNECESSARY.
|
||||
*
|
||||
* struct tcp6_sock *bpf_skc_to_tcp6_sock(void *sk)
|
||||
* Description
|
||||
* Dynamically cast a *sk* pointer to a *tcp6_sock* pointer.
|
||||
* Return
|
||||
* *sk* if casting is valid, or NULL otherwise.
|
||||
*/
|
||||
#define __BPF_FUNC_MAPPER(FN) \
|
||||
FN(unspec), \
|
||||
@ -3392,7 +3398,8 @@ union bpf_attr {
|
||||
FN(ringbuf_submit), \
|
||||
FN(ringbuf_discard), \
|
||||
FN(ringbuf_query), \
|
||||
FN(csum_level),
|
||||
FN(csum_level), \
|
||||
FN(skc_to_tcp6_sock),
|
||||
|
||||
/* integer value in 'imm' field of BPF_CALL instruction selects which helper
|
||||
* function eBPF program intends to call
|
||||
|
Loading…
Reference in New Issue
Block a user