net: sock: allow eBPF programs to be attached to sockets
authorAlexei Starovoitov <ast@plumgrid.com>
Mon, 1 Dec 2014 23:06:35 +0000 (15:06 -0800)
committerDavid S. Miller <davem@davemloft.net>
Sat, 6 Dec 2014 05:47:32 +0000 (21:47 -0800)
introduce new setsockopt() command:

setsockopt(sock, SOL_SOCKET, SO_ATTACH_BPF, &prog_fd, sizeof(prog_fd))

where prog_fd was received from syscall bpf(BPF_PROG_LOAD, attr, ...)
and attr->prog_type == BPF_PROG_TYPE_SOCKET_FILTER

setsockopt() calls bpf_prog_get() which increments refcnt of the program,
so it doesn't get unloaded while socket is using the program.

The same eBPF program can be attached to multiple sockets.

User task exit automatically closes socket which calls sk_filter_uncharge()
which decrements refcnt of eBPF program

Signed-off-by: Alexei Starovoitov <ast@plumgrid.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
18 files changed:
arch/alpha/include/uapi/asm/socket.h
arch/avr32/include/uapi/asm/socket.h
arch/cris/include/uapi/asm/socket.h
arch/frv/include/uapi/asm/socket.h
arch/ia64/include/uapi/asm/socket.h
arch/m32r/include/uapi/asm/socket.h
arch/mips/include/uapi/asm/socket.h
arch/mn10300/include/uapi/asm/socket.h
arch/parisc/include/uapi/asm/socket.h
arch/powerpc/include/uapi/asm/socket.h
arch/s390/include/uapi/asm/socket.h
arch/sparc/include/uapi/asm/socket.h
arch/xtensa/include/uapi/asm/socket.h
include/linux/bpf.h
include/linux/filter.h
include/uapi/asm-generic/socket.h
net/core/filter.c
net/core/sock.c

index e2fe070..9a20821 100644 (file)
@@ -89,4 +89,7 @@
 
 #define SO_INCOMING_CPU                49
 
+#define SO_ATTACH_BPF          50
+#define SO_DETACH_BPF          SO_DETACH_FILTER
+
 #endif /* _UAPI_ASM_SOCKET_H */
index 92121b0..2b65ed6 100644 (file)
@@ -82,4 +82,7 @@
 
 #define SO_INCOMING_CPU                49
 
+#define SO_ATTACH_BPF          50
+#define SO_DETACH_BPF          SO_DETACH_FILTER
+
 #endif /* _UAPI__ASM_AVR32_SOCKET_H */
index 60f60f5..e2503d9 100644 (file)
@@ -84,6 +84,9 @@
 
 #define SO_INCOMING_CPU                49
 
+#define SO_ATTACH_BPF          50
+#define SO_DETACH_BPF          SO_DETACH_FILTER
+
 #endif /* _ASM_SOCKET_H */
 
 
index 2c68902..4823ad1 100644 (file)
@@ -82,5 +82,8 @@
 
 #define SO_INCOMING_CPU                49
 
+#define SO_ATTACH_BPF          50
+#define SO_DETACH_BPF          SO_DETACH_FILTER
+
 #endif /* _ASM_SOCKET_H */
 
index 09a93fb..59be3d8 100644 (file)
@@ -91,4 +91,7 @@
 
 #define SO_INCOMING_CPU                49
 
+#define SO_ATTACH_BPF          50
+#define SO_DETACH_BPF          SO_DETACH_FILTER
+
 #endif /* _ASM_IA64_SOCKET_H */
index e858981..7bc4cb2 100644 (file)
@@ -82,4 +82,7 @@
 
 #define SO_INCOMING_CPU                49
 
+#define SO_ATTACH_BPF          50
+#define SO_DETACH_BPF          SO_DETACH_FILTER
+
 #endif /* _ASM_M32R_SOCKET_H */
index 2e9ee8c..dec3c85 100644 (file)
 
 #define SO_INCOMING_CPU                49
 
+#define SO_ATTACH_BPF          50
+#define SO_DETACH_BPF          SO_DETACH_FILTER
+
 #endif /* _UAPI_ASM_SOCKET_H */
index f3492e8..cab7d6d 100644 (file)
@@ -82,4 +82,7 @@
 
 #define SO_INCOMING_CPU                49
 
+#define SO_ATTACH_BPF          50
+#define SO_DETACH_BPF          SO_DETACH_FILTER
+
 #endif /* _ASM_SOCKET_H */
index 7984a1c..a5cd40c 100644 (file)
@@ -81,4 +81,7 @@
 
 #define SO_INCOMING_CPU                0x402A
 
+#define SO_ATTACH_BPF          0x402B
+#define SO_DETACH_BPF          SO_DETACH_FILTER
+
 #endif /* _UAPI_ASM_SOCKET_H */
index 3474e4e..c046666 100644 (file)
@@ -89,4 +89,7 @@
 
 #define SO_INCOMING_CPU                49
 
+#define SO_ATTACH_BPF          50
+#define SO_DETACH_BPF          SO_DETACH_FILTER
+
 #endif /* _ASM_POWERPC_SOCKET_H */
index 8457636..296942d 100644 (file)
@@ -88,4 +88,7 @@
 
 #define SO_INCOMING_CPU                49
 
+#define SO_ATTACH_BPF          50
+#define SO_DETACH_BPF          SO_DETACH_FILTER
+
 #endif /* _ASM_SOCKET_H */
index 4a8003a..e6a16c4 100644 (file)
@@ -78,6 +78,9 @@
 
 #define SO_INCOMING_CPU                0x0033
 
+#define SO_ATTACH_BPF          0x0034
+#define SO_DETACH_BPF          SO_DETACH_FILTER
+
 /* Security levels - as per NRL IPv6 - don't actually do anything */
 #define SO_SECURITY_AUTHENTICATION             0x5001
 #define SO_SECURITY_ENCRYPTION_TRANSPORT       0x5002
index c46f6a6..4120af0 100644 (file)
@@ -93,4 +93,7 @@
 
 #define SO_INCOMING_CPU                49
 
+#define SO_ATTACH_BPF          50
+#define SO_DETACH_BPF          SO_DETACH_FILTER
+
 #endif /* _XTENSA_SOCKET_H */
index 75e94ea..bbfceb7 100644 (file)
@@ -128,7 +128,11 @@ struct bpf_prog_aux {
        struct work_struct work;
 };
 
+#ifdef CONFIG_BPF_SYSCALL
 void bpf_prog_put(struct bpf_prog *prog);
+#else
+static inline void bpf_prog_put(struct bpf_prog *prog) {}
+#endif
 struct bpf_prog *bpf_prog_get(u32 ufd);
 /* verify correctness of eBPF program */
 int bpf_check(struct bpf_prog *fp, union bpf_attr *attr);
index ca95abd..caac208 100644 (file)
@@ -381,6 +381,7 @@ int bpf_prog_create(struct bpf_prog **pfp, struct sock_fprog_kern *fprog);
 void bpf_prog_destroy(struct bpf_prog *fp);
 
 int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk);
+int sk_attach_bpf(u32 ufd, struct sock *sk);
 int sk_detach_filter(struct sock *sk);
 
 int bpf_check_classic(const struct sock_filter *filter, unsigned int flen);
index f541cce..5c15c2a 100644 (file)
@@ -84,4 +84,7 @@
 
 #define SO_INCOMING_CPU                49
 
+#define SO_ATTACH_BPF          50
+#define SO_DETACH_BPF          SO_DETACH_FILTER
+
 #endif /* __ASM_GENERIC_SOCKET_H */
index 647b122..8cc3c03 100644 (file)
@@ -44,6 +44,7 @@
 #include <linux/ratelimit.h>
 #include <linux/seccomp.h>
 #include <linux/if_vlan.h>
+#include <linux/bpf.h>
 
 /**
  *     sk_filter - run a packet through a socket filter
@@ -813,8 +814,12 @@ static void bpf_release_orig_filter(struct bpf_prog *fp)
 
 static void __bpf_prog_release(struct bpf_prog *prog)
 {
-       bpf_release_orig_filter(prog);
-       bpf_prog_free(prog);
+       if (prog->aux->prog_type == BPF_PROG_TYPE_SOCKET_FILTER) {
+               bpf_prog_put(prog);
+       } else {
+               bpf_release_orig_filter(prog);
+               bpf_prog_free(prog);
+       }
 }
 
 static void __sk_filter_release(struct sk_filter *fp)
@@ -1088,6 +1093,94 @@ int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk)
 }
 EXPORT_SYMBOL_GPL(sk_attach_filter);
 
+#ifdef CONFIG_BPF_SYSCALL
+int sk_attach_bpf(u32 ufd, struct sock *sk)
+{
+       struct sk_filter *fp, *old_fp;
+       struct bpf_prog *prog;
+
+       if (sock_flag(sk, SOCK_FILTER_LOCKED))
+               return -EPERM;
+
+       prog = bpf_prog_get(ufd);
+       if (!prog)
+               return -EINVAL;
+
+       if (prog->aux->prog_type != BPF_PROG_TYPE_SOCKET_FILTER) {
+               /* valid fd, but invalid program type */
+               bpf_prog_put(prog);
+               return -EINVAL;
+       }
+
+       fp = kmalloc(sizeof(*fp), GFP_KERNEL);
+       if (!fp) {
+               bpf_prog_put(prog);
+               return -ENOMEM;
+       }
+       fp->prog = prog;
+
+       atomic_set(&fp->refcnt, 0);
+
+       if (!sk_filter_charge(sk, fp)) {
+               __sk_filter_release(fp);
+               return -ENOMEM;
+       }
+
+       old_fp = rcu_dereference_protected(sk->sk_filter,
+                                          sock_owned_by_user(sk));
+       rcu_assign_pointer(sk->sk_filter, fp);
+
+       if (old_fp)
+               sk_filter_uncharge(sk, old_fp);
+
+       return 0;
+}
+
+/* allow socket filters to call
+ * bpf_map_lookup_elem(), bpf_map_update_elem(), bpf_map_delete_elem()
+ */
+static const struct bpf_func_proto *sock_filter_func_proto(enum bpf_func_id func_id)
+{
+       switch (func_id) {
+       case BPF_FUNC_map_lookup_elem:
+               return &bpf_map_lookup_elem_proto;
+       case BPF_FUNC_map_update_elem:
+               return &bpf_map_update_elem_proto;
+       case BPF_FUNC_map_delete_elem:
+               return &bpf_map_delete_elem_proto;
+       default:
+               return NULL;
+       }
+}
+
+static bool sock_filter_is_valid_access(int off, int size, enum bpf_access_type type)
+{
+       /* skb fields cannot be accessed yet */
+       return false;
+}
+
+static struct bpf_verifier_ops sock_filter_ops = {
+       .get_func_proto = sock_filter_func_proto,
+       .is_valid_access = sock_filter_is_valid_access,
+};
+
+static struct bpf_prog_type_list tl = {
+       .ops = &sock_filter_ops,
+       .type = BPF_PROG_TYPE_SOCKET_FILTER,
+};
+
+static int __init register_sock_filter_ops(void)
+{
+       bpf_register_prog_type(&tl);
+       return 0;
+}
+late_initcall(register_sock_filter_ops);
+#else
+int sk_attach_bpf(u32 ufd, struct sock *sk)
+{
+       return -EOPNOTSUPP;
+}
+#endif
 int sk_detach_filter(struct sock *sk)
 {
        int ret = -ENOENT;
index 0725cf0..9a56b20 100644 (file)
@@ -888,6 +888,19 @@ set_rcvbuf:
                }
                break;
 
+       case SO_ATTACH_BPF:
+               ret = -EINVAL;
+               if (optlen == sizeof(u32)) {
+                       u32 ufd;
+
+                       ret = -EFAULT;
+                       if (copy_from_user(&ufd, optval, sizeof(ufd)))
+                               break;
+
+                       ret = sk_attach_bpf(ufd, sk);
+               }
+               break;
+
        case SO_DETACH_FILTER:
                ret = sk_detach_filter(sk);
                break;