bpf: add BPF_PROG_ATTACH and BPF_PROG_DETACH commands
authorDaniel Mack <daniel@zonque.org>
Wed, 23 Nov 2016 15:52:27 +0000 (16:52 +0100)
committerDavid S. Miller <davem@davemloft.net>
Fri, 25 Nov 2016 21:26:04 +0000 (16:26 -0500)
Extend the bpf(2) syscall by two new commands, BPF_PROG_ATTACH and
BPF_PROG_DETACH which allow attaching and detaching eBPF programs
to a target.

On the API level, the target could be anything that has an fd in
userspace, hence the name of the field in union bpf_attr is called
'target_fd'.

When called with BPF_ATTACH_TYPE_CGROUP_INET_{E,IN}GRESS, the target is
expected to be a valid file descriptor of a cgroup v2 directory which
has the bpf controller enabled. These are the only use-cases
implemented by this patch at this point, but more can be added.

If a program of the given type already exists in the given cgroup,
the program is swapped automically, so userspace does not have to drop
an existing program first before installing a new one, which would
otherwise leave a gap in which no program is attached.

For more information on the propagation logic to subcgroups, please
refer to the bpf cgroup controller implementation.

The API is guarded by CAP_NET_ADMIN.

Signed-off-by: Daniel Mack <daniel@zonque.org>
Acked-by: Alexei Starovoitov <ast@kernel.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/uapi/linux/bpf.h
kernel/bpf/syscall.c

index 5ae679f..1370a9d 100644 (file)
@@ -73,6 +73,8 @@ enum bpf_cmd {
        BPF_PROG_LOAD,
        BPF_OBJ_PIN,
        BPF_OBJ_GET,
+       BPF_PROG_ATTACH,
+       BPF_PROG_DETACH,
 };
 
 enum bpf_map_type {
@@ -159,6 +161,12 @@ union bpf_attr {
                __aligned_u64   pathname;
                __u32           bpf_fd;
        };
+
+       struct { /* anonymous struct used by BPF_PROG_ATTACH/DETACH commands */
+               __u32           target_fd;      /* container object to attach to */
+               __u32           attach_bpf_fd;  /* eBPF program to attach */
+               __u32           attach_type;
+       };
 } __attribute__((aligned(8)));
 
 /* BPF helper function descriptions:
index eb15498..1090d16 100644 (file)
@@ -835,6 +835,77 @@ static int bpf_obj_get(const union bpf_attr *attr)
        return bpf_obj_get_user(u64_to_user_ptr(attr->pathname));
 }
 
+#ifdef CONFIG_CGROUP_BPF
+
+#define BPF_PROG_ATTACH_LAST_FIELD attach_type
+
+static int bpf_prog_attach(const union bpf_attr *attr)
+{
+       struct bpf_prog *prog;
+       struct cgroup *cgrp;
+
+       if (!capable(CAP_NET_ADMIN))
+               return -EPERM;
+
+       if (CHECK_ATTR(BPF_PROG_ATTACH))
+               return -EINVAL;
+
+       switch (attr->attach_type) {
+       case BPF_CGROUP_INET_INGRESS:
+       case BPF_CGROUP_INET_EGRESS:
+               prog = bpf_prog_get_type(attr->attach_bpf_fd,
+                                        BPF_PROG_TYPE_CGROUP_SKB);
+               if (IS_ERR(prog))
+                       return PTR_ERR(prog);
+
+               cgrp = cgroup_get_from_fd(attr->target_fd);
+               if (IS_ERR(cgrp)) {
+                       bpf_prog_put(prog);
+                       return PTR_ERR(cgrp);
+               }
+
+               cgroup_bpf_update(cgrp, prog, attr->attach_type);
+               cgroup_put(cgrp);
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+#define BPF_PROG_DETACH_LAST_FIELD attach_type
+
+static int bpf_prog_detach(const union bpf_attr *attr)
+{
+       struct cgroup *cgrp;
+
+       if (!capable(CAP_NET_ADMIN))
+               return -EPERM;
+
+       if (CHECK_ATTR(BPF_PROG_DETACH))
+               return -EINVAL;
+
+       switch (attr->attach_type) {
+       case BPF_CGROUP_INET_INGRESS:
+       case BPF_CGROUP_INET_EGRESS:
+               cgrp = cgroup_get_from_fd(attr->target_fd);
+               if (IS_ERR(cgrp))
+                       return PTR_ERR(cgrp);
+
+               cgroup_bpf_update(cgrp, NULL, attr->attach_type);
+               cgroup_put(cgrp);
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+#endif /* CONFIG_CGROUP_BPF */
+
 SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, size)
 {
        union bpf_attr attr = {};
@@ -901,6 +972,16 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz
        case BPF_OBJ_GET:
                err = bpf_obj_get(&attr);
                break;
+
+#ifdef CONFIG_CGROUP_BPF
+       case BPF_PROG_ATTACH:
+               err = bpf_prog_attach(&attr);
+               break;
+       case BPF_PROG_DETACH:
+               err = bpf_prog_detach(&attr);
+               break;
+#endif
+
        default:
                err = -EINVAL;
                break;