soreuseport: setsockopt SO_ATTACH_REUSEPORT_[CE]BPF
[cascardo/linux.git] / net / core / filter.c
index b513eb8..35e6fed 100644 (file)
@@ -50,6 +50,7 @@
 #include <net/cls_cgroup.h>
 #include <net/dst_metadata.h>
 #include <net/dst.h>
+#include <net/sock_reuseport.h>
 
 /**
  *     sk_filter - run a packet through a socket filter
@@ -348,12 +349,6 @@ static bool convert_bpf_extensions(struct sock_filter *fp,
  *    jump offsets, 2nd pass remapping:
  *   new_prog = kmalloc(sizeof(struct bpf_insn) * new_len);
  *   bpf_convert_filter(old_prog, old_len, new_prog, &new_len);
- *
- * User BPF's register A is mapped to our BPF register 6, user BPF
- * register X is mapped to BPF register 7; frame pointer is always
- * register 10; Context 'void *ctx' is stored in register 1, that is,
- * for socket filters: ctx == 'struct sk_buff *', for seccomp:
- * ctx == 'struct seccomp_data *'.
  */
 static int bpf_convert_filter(struct sock_filter *prog, int len,
                              struct bpf_insn *new_prog, int *new_len)
@@ -1173,17 +1168,32 @@ static int __sk_attach_prog(struct bpf_prog *prog, struct sock *sk)
        return 0;
 }
 
-/**
- *     sk_attach_filter - attach a socket filter
- *     @fprog: the filter program
- *     @sk: the socket to use
- *
- * Attach the user's filter code. We first run some sanity checks on
- * it to make sure it does not explode on us later. If an error
- * occurs or there is insufficient memory for the filter a negative
- * errno code is returned. On success the return is zero.
- */
-int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk)
+static int __reuseport_attach_prog(struct bpf_prog *prog, struct sock *sk)
+{
+       struct bpf_prog *old_prog;
+       int err;
+
+       if (bpf_prog_size(prog->len) > sysctl_optmem_max)
+               return -ENOMEM;
+
+       if (sk_unhashed(sk)) {
+               err = reuseport_alloc(sk);
+               if (err)
+                       return err;
+       } else if (!rcu_access_pointer(sk->sk_reuseport_cb)) {
+               /* The socket wasn't bound with SO_REUSEPORT */
+               return -EINVAL;
+       }
+
+       old_prog = reuseport_attach_prog(sk, prog);
+       if (old_prog)
+               bpf_prog_destroy(old_prog);
+
+       return 0;
+}
+
+static
+struct bpf_prog *__get_filter(struct sock_fprog *fprog, struct sock *sk)
 {
        unsigned int fsize = bpf_classic_proglen(fprog);
        unsigned int bpf_fsize = bpf_prog_size(fprog->len);
@@ -1191,19 +1201,19 @@ int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk)
        int err;
 
        if (sock_flag(sk, SOCK_FILTER_LOCKED))
-               return -EPERM;
+               return ERR_PTR(-EPERM);
 
        /* Make sure new filter is there and in the right amounts. */
        if (fprog->filter == NULL)
-               return -EINVAL;
+               return ERR_PTR(-EINVAL);
 
        prog = bpf_prog_alloc(bpf_fsize, 0);
        if (!prog)
-               return -ENOMEM;
+               return ERR_PTR(-ENOMEM);
 
        if (copy_from_user(prog->insns, fprog->filter, fsize)) {
                __bpf_prog_free(prog);
-               return -EFAULT;
+               return ERR_PTR(-EFAULT);
        }
 
        prog->len = fprog->len;
@@ -1211,13 +1221,30 @@ int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk)
        err = bpf_prog_store_orig_filter(prog, fprog);
        if (err) {
                __bpf_prog_free(prog);
-               return -ENOMEM;
+               return ERR_PTR(-ENOMEM);
        }
 
        /* bpf_prepare_filter() already takes care of freeing
         * memory in case something goes wrong.
         */
-       prog = bpf_prepare_filter(prog, NULL);
+       return bpf_prepare_filter(prog, NULL);
+}
+
+/**
+ *     sk_attach_filter - attach a socket filter
+ *     @fprog: the filter program
+ *     @sk: the socket to use
+ *
+ * Attach the user's filter code. We first run some sanity checks on
+ * it to make sure it does not explode on us later. If an error
+ * occurs or there is insufficient memory for the filter a negative
+ * errno code is returned. On success the return is zero.
+ */
+int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk)
+{
+       struct bpf_prog *prog = __get_filter(fprog, sk);
+       int err;
+
        if (IS_ERR(prog))
                return PTR_ERR(prog);
 
@@ -1231,23 +1258,50 @@ int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk)
 }
 EXPORT_SYMBOL_GPL(sk_attach_filter);
 
-int sk_attach_bpf(u32 ufd, struct sock *sk)
+int sk_reuseport_attach_filter(struct sock_fprog *fprog, struct sock *sk)
 {
-       struct bpf_prog *prog;
+       struct bpf_prog *prog = __get_filter(fprog, sk);
        int err;
 
+       if (IS_ERR(prog))
+               return PTR_ERR(prog);
+
+       err = __reuseport_attach_prog(prog, sk);
+       if (err < 0) {
+               __bpf_prog_release(prog);
+               return err;
+       }
+
+       return 0;
+}
+
+static struct bpf_prog *__get_bpf(u32 ufd, struct sock *sk)
+{
+       struct bpf_prog *prog;
+
        if (sock_flag(sk, SOCK_FILTER_LOCKED))
-               return -EPERM;
+               return ERR_PTR(-EPERM);
 
        prog = bpf_prog_get(ufd);
        if (IS_ERR(prog))
-               return PTR_ERR(prog);
+               return prog;
 
        if (prog->type != BPF_PROG_TYPE_SOCKET_FILTER) {
                bpf_prog_put(prog);
-               return -EINVAL;
+               return ERR_PTR(-EINVAL);
        }
 
+       return prog;
+}
+
+int sk_attach_bpf(u32 ufd, struct sock *sk)
+{
+       struct bpf_prog *prog = __get_bpf(ufd, sk);
+       int err;
+
+       if (IS_ERR(prog))
+               return PTR_ERR(prog);
+
        err = __sk_attach_prog(prog, sk);
        if (err < 0) {
                bpf_prog_put(prog);
@@ -1257,6 +1311,23 @@ int sk_attach_bpf(u32 ufd, struct sock *sk)
        return 0;
 }
 
+int sk_reuseport_attach_bpf(u32 ufd, struct sock *sk)
+{
+       struct bpf_prog *prog = __get_bpf(ufd, sk);
+       int err;
+
+       if (IS_ERR(prog))
+               return PTR_ERR(prog);
+
+       err = __reuseport_attach_prog(prog, sk);
+       if (err < 0) {
+               bpf_prog_put(prog);
+               return err;
+       }
+
+       return 0;
+}
+
 #define BPF_RECOMPUTE_CSUM(flags)      ((flags) & 1)
 #define BPF_LDST_LEN                   16U