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 e2fe0700b3b442bffc1f606b1b8b0bb7759aa157..9a20821b111c65aecc5b60a7956fb00dde178aae 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 92121b0f5b989a61c008e0be24030725bab88e36..2b65ed6b277cada8728d70f26f3df76c40af078b 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 60f60f5b9b35bd219d7a9834fe5394e8ac5fdbab..e2503d9f1869b4e7fead3a5467581169025a80a1 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 2c6890209ea60c149bf097c2a1b369519cb8c301..4823ad125578246f1b66b3e59003cd542b790cf5 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 09a93fb566f6c6c6fe29c10c95b931881843d1cd..59be3d87f86d660d5477b1b07c17d61697e642e8 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 e8589819c2743c6e112b15a245fc3ebd146e6313..7bc4cb27385658014b0d258bc534b24d6e93822f 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 2e9ee8c55a103a0337d9f80f71fe9ef28be1154b..dec3c850f36be661ba80f57ef651dd16ff44466a 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 f3492e8c9f7009c33e07168df916f7337bef3929..cab7d6d50051b1af1533630567d9114ab9b7ddb8 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 7984a1cab3da980f1f810827967b4b67616eb89b..a5cd40cd8ee1cd53109c07d9b94543d9d7045c97 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 3474e4ef166df4a573773916b325d0fa9f3b45d0..c046666038f85b7c09f1f766affe7f561be1590b 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 8457636c33e1b67a9b7804daa05627839035a8fb..296942d56e6a077b2411cb3738b68e66870d3152 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 4a8003a9416348006cfa85d5bcdf7553c8d23958..e6a16c40be5f0d548ca6c0a5127b0c1a85dca890 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 c46f6a696849c6f7f8a34b2cc522b48e04b17380..4120af08616055708c8b64c70d4d256100d49ac7 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 75e94eaa228b6ce6b175b2d7ce3c562c856c8e56..bbfceb7564523bc5e17037e25663185aee02311e 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 ca95abd2bed130ac3c76161e7ae273e59c48c51b..caac2087a4d5e7479e272b6dc5da23d7c90c9fd7 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 f541ccefd4acbeb4ad757be9dbf4b67f204bf21d..5c15c2a5c1235157e75741cdcef290c61709f3a8 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 647b12265e181b3ab650670f5572caa8a0f802a6..8cc3c03078b357003586d6dc4f9e10ac4e1d70f8 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 0725cf0cb685787b2122606437da53299fb24621..9a56b2000c3f374fb95aedada3327447816a9512 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;