OSDN Git Service

bpf: Add bpf_skc_to_tcp6_sock() helper
authorYonghong Song <yhs@fb.com>
Tue, 23 Jun 2020 23:08:09 +0000 (16:08 -0700)
committerAlexei Starovoitov <ast@kernel.org>
Thu, 25 Jun 2020 01:37:59 +0000 (18:37 -0700)
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
include/linux/bpf.h
include/uapi/linux/bpf.h
kernel/bpf/btf.c
kernel/bpf/verifier.c
kernel/trace/bpf_trace.c
net/core/filter.c
scripts/bpf_helpers_doc.py
tools/include/uapi/linux/bpf.h

index 1e1501e..c0e38ad 100644 (file)
@@ -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);
index d9737d5..e90ad07 100644 (file)
@@ -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
index e377d19..4c3007f 100644 (file)
@@ -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);
index 7460f96..7de9890 100644 (file)
@@ -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 = &regs[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);
index 0159f12..2a97a26 100644 (file)
@@ -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 ?
index c713b6b..176e27d 100644 (file)
@@ -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],
+};
index 91fa668..6c2f641 100755 (executable)
@@ -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',
index d9737d5..e90ad07 100644 (file)
@@ -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