bpf: introduce BPF_PROG_QUERY command
authorAlexei Starovoitov <ast@fb.com>
Tue, 3 Oct 2017 05:50:22 +0000 (22:50 -0700)
committerDavid S. Miller <davem@davemloft.net>
Wed, 4 Oct 2017 23:05:05 +0000 (16:05 -0700)
introduce BPF_PROG_QUERY command to retrieve a set of either
attached programs to given cgroup or a set of effective programs
that will execute for events within a cgroup

Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Acked-by: Daniel Borkmann <daniel@iogearbox.net>
Acked-by: Martin KaFai Lau <kafai@fb.com>
for cgroup bits
Acked-by: Tejun Heo <tj@kernel.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/linux/bpf-cgroup.h
include/linux/bpf.h
include/uapi/linux/bpf.h
kernel/bpf/cgroup.c
kernel/bpf/core.c
kernel/bpf/syscall.c
kernel/cgroup/cgroup.c

index 102e56fbb6dea46f47614a8ec6b35522fe45958b..359b6f5d3d90c8cbefa2307cc8c6df05e4918c57 100644 (file)
@@ -44,12 +44,16 @@ int __cgroup_bpf_attach(struct cgroup *cgrp, struct bpf_prog *prog,
                        enum bpf_attach_type type, u32 flags);
 int __cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog,
                        enum bpf_attach_type type, u32 flags);
+int __cgroup_bpf_query(struct cgroup *cgrp, const union bpf_attr *attr,
+                      union bpf_attr __user *uattr);
 
 /* Wrapper for __cgroup_bpf_*() protected by cgroup_mutex */
 int cgroup_bpf_attach(struct cgroup *cgrp, struct bpf_prog *prog,
                      enum bpf_attach_type type, u32 flags);
 int cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog,
                      enum bpf_attach_type type, u32 flags);
+int cgroup_bpf_query(struct cgroup *cgrp, const union bpf_attr *attr,
+                    union bpf_attr __user *uattr);
 
 int __cgroup_bpf_run_filter_skb(struct sock *sk,
                                struct sk_buff *skb,
index a6964b75f0706bcc58ace2d52c27a046b900932d..a67daea731ab8b7f76777f3a5bdaf90d7fc383b9 100644 (file)
@@ -260,6 +260,9 @@ struct bpf_prog_array {
 
 struct bpf_prog_array __rcu *bpf_prog_array_alloc(u32 prog_cnt, gfp_t flags);
 void bpf_prog_array_free(struct bpf_prog_array __rcu *progs);
+int bpf_prog_array_length(struct bpf_prog_array __rcu *progs);
+int bpf_prog_array_copy_to_user(struct bpf_prog_array __rcu *progs,
+                               __u32 __user *prog_ids, u32 cnt);
 
 #define BPF_PROG_RUN_ARRAY(array, ctx, func)           \
        ({                                              \
index 762f74bc6c479c6b511faa4077e2507ce1f4c659..cb2b9f95160aa5224eccc115d0cd5002241acb37 100644 (file)
@@ -92,6 +92,7 @@ enum bpf_cmd {
        BPF_PROG_GET_FD_BY_ID,
        BPF_MAP_GET_FD_BY_ID,
        BPF_OBJ_GET_INFO_BY_FD,
+       BPF_PROG_QUERY,
 };
 
 enum bpf_map_type {
@@ -211,6 +212,9 @@ enum bpf_attach_type {
 /* Specify numa node during map creation */
 #define BPF_F_NUMA_NODE                (1U << 2)
 
+/* flags for BPF_PROG_QUERY */
+#define BPF_F_QUERY_EFFECTIVE  (1U << 0)
+
 #define BPF_OBJ_NAME_LEN 16U
 
 union bpf_attr {
@@ -289,6 +293,15 @@ union bpf_attr {
                __u32           info_len;
                __aligned_u64   info;
        } info;
+
+       struct { /* anonymous struct used by BPF_PROG_QUERY command */
+               __u32           target_fd;      /* container object to query */
+               __u32           attach_type;
+               __u32           query_flags;
+               __u32           attach_flags;
+               __aligned_u64   prog_ids;
+               __u32           prog_cnt;
+       } query;
 } __attribute__((aligned(8)));
 
 /* BPF helper function descriptions:
index 6b7500bbdb5378315fa116b9a5c96575e5226c42..e88abc0865d506ac67c3f3eda574412a48a8426a 100644 (file)
@@ -384,6 +384,52 @@ cleanup:
        return err;
 }
 
+/* Must be called with cgroup_mutex held to avoid races. */
+int __cgroup_bpf_query(struct cgroup *cgrp, const union bpf_attr *attr,
+                      union bpf_attr __user *uattr)
+{
+       __u32 __user *prog_ids = u64_to_user_ptr(attr->query.prog_ids);
+       enum bpf_attach_type type = attr->query.attach_type;
+       struct list_head *progs = &cgrp->bpf.progs[type];
+       u32 flags = cgrp->bpf.flags[type];
+       int cnt, ret = 0, i;
+
+       if (attr->query.query_flags & BPF_F_QUERY_EFFECTIVE)
+               cnt = bpf_prog_array_length(cgrp->bpf.effective[type]);
+       else
+               cnt = prog_list_length(progs);
+
+       if (copy_to_user(&uattr->query.attach_flags, &flags, sizeof(flags)))
+               return -EFAULT;
+       if (copy_to_user(&uattr->query.prog_cnt, &cnt, sizeof(cnt)))
+               return -EFAULT;
+       if (attr->query.prog_cnt == 0 || !prog_ids || !cnt)
+               /* return early if user requested only program count + flags */
+               return 0;
+       if (attr->query.prog_cnt < cnt) {
+               cnt = attr->query.prog_cnt;
+               ret = -ENOSPC;
+       }
+
+       if (attr->query.query_flags & BPF_F_QUERY_EFFECTIVE) {
+               return bpf_prog_array_copy_to_user(cgrp->bpf.effective[type],
+                                                  prog_ids, cnt);
+       } else {
+               struct bpf_prog_list *pl;
+               u32 id;
+
+               i = 0;
+               list_for_each_entry(pl, progs, node) {
+                       id = pl->prog->aux->id;
+                       if (copy_to_user(prog_ids + i, &id, sizeof(id)))
+                               return -EFAULT;
+                       if (++i == cnt)
+                               break;
+               }
+       }
+       return ret;
+}
+
 /**
  * __cgroup_bpf_run_filter_skb() - Run a program for packet filtering
  * @sk: The socket sending or receiving traffic
index 6b49e1991ae7ad0afda5ac706280e94bb53be12b..eba966c09053f9c6c68ba1a66a348c0242b85cf3 100644 (file)
@@ -1412,6 +1412,44 @@ void bpf_prog_array_free(struct bpf_prog_array __rcu *progs)
        kfree_rcu(progs, rcu);
 }
 
+int bpf_prog_array_length(struct bpf_prog_array __rcu *progs)
+{
+       struct bpf_prog **prog;
+       u32 cnt = 0;
+
+       rcu_read_lock();
+       prog = rcu_dereference(progs)->progs;
+       for (; *prog; prog++)
+               cnt++;
+       rcu_read_unlock();
+       return cnt;
+}
+
+int bpf_prog_array_copy_to_user(struct bpf_prog_array __rcu *progs,
+                               __u32 __user *prog_ids, u32 cnt)
+{
+       struct bpf_prog **prog;
+       u32 i = 0, id;
+
+       rcu_read_lock();
+       prog = rcu_dereference(progs)->progs;
+       for (; *prog; prog++) {
+               id = (*prog)->aux->id;
+               if (copy_to_user(prog_ids + i, &id, sizeof(id))) {
+                       rcu_read_unlock();
+                       return -EFAULT;
+               }
+               if (++i == cnt) {
+                       prog++;
+                       break;
+               }
+       }
+       rcu_read_unlock();
+       if (*prog)
+               return -ENOSPC;
+       return 0;
+}
+
 static void bpf_prog_free_deferred(struct work_struct *work)
 {
        struct bpf_prog_aux *aux;
index 51bee695d32c24c1fb9339f6102ccfe7795d1b32..0048cb24ba7b43eba7c61b6ec07fde54fc9657a5 100644 (file)
@@ -1272,6 +1272,37 @@ static int bpf_prog_detach(const union bpf_attr *attr)
        return ret;
 }
 
+#define BPF_PROG_QUERY_LAST_FIELD query.prog_cnt
+
+static int bpf_prog_query(const union bpf_attr *attr,
+                         union bpf_attr __user *uattr)
+{
+       struct cgroup *cgrp;
+       int ret;
+
+       if (!capable(CAP_NET_ADMIN))
+               return -EPERM;
+       if (CHECK_ATTR(BPF_PROG_QUERY))
+               return -EINVAL;
+       if (attr->query.query_flags & ~BPF_F_QUERY_EFFECTIVE)
+               return -EINVAL;
+
+       switch (attr->query.attach_type) {
+       case BPF_CGROUP_INET_INGRESS:
+       case BPF_CGROUP_INET_EGRESS:
+       case BPF_CGROUP_INET_SOCK_CREATE:
+       case BPF_CGROUP_SOCK_OPS:
+               break;
+       default:
+               return -EINVAL;
+       }
+       cgrp = cgroup_get_from_fd(attr->query.target_fd);
+       if (IS_ERR(cgrp))
+               return PTR_ERR(cgrp);
+       ret = cgroup_bpf_query(cgrp, attr, uattr);
+       cgroup_put(cgrp);
+       return ret;
+}
 #endif /* CONFIG_CGROUP_BPF */
 
 #define BPF_PROG_TEST_RUN_LAST_FIELD test.duration
@@ -1568,6 +1599,9 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz
        case BPF_PROG_DETACH:
                err = bpf_prog_detach(&attr);
                break;
+       case BPF_PROG_QUERY:
+               err = bpf_prog_query(&attr, uattr);
+               break;
 #endif
        case BPF_PROG_TEST_RUN:
                err = bpf_prog_test_run(&attr, uattr);
index 57eb866ae78d5b568c327b30c06cb13854da7bee..269512b94a947350423903d38db49030f5b9e997 100644 (file)
@@ -5761,4 +5761,14 @@ int cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog,
        mutex_unlock(&cgroup_mutex);
        return ret;
 }
+int cgroup_bpf_query(struct cgroup *cgrp, const union bpf_attr *attr,
+                    union bpf_attr __user *uattr)
+{
+       int ret;
+
+       mutex_lock(&cgroup_mutex);
+       ret = __cgroup_bpf_query(cgrp, attr, uattr);
+       mutex_unlock(&cgroup_mutex);
+       return ret;
+}
 #endif /* CONFIG_CGROUP_BPF */