Merge git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next
[linux-2.6-microblaze.git] / net / core / filter.c
index 2bf6624..3fa16b8 100644 (file)
@@ -9252,61 +9252,205 @@ const struct bpf_verifier_ops sk_reuseport_verifier_ops = {
 
 const struct bpf_prog_ops sk_reuseport_prog_ops = {
 };
-#endif /* CONFIG_INET */
 
-DEFINE_BPF_DISPATCHER(xdp)
+DEFINE_STATIC_KEY_FALSE(bpf_sk_lookup_enabled);
+EXPORT_SYMBOL(bpf_sk_lookup_enabled);
 
-void bpf_prog_change_xdp(struct bpf_prog *prev_prog, struct bpf_prog *prog)
+BPF_CALL_3(bpf_sk_lookup_assign, struct bpf_sk_lookup_kern *, ctx,
+          struct sock *, sk, u64, flags)
 {
-       bpf_dispatcher_change_prog(BPF_DISPATCHER_PTR(xdp), prev_prog, prog);
+       if (unlikely(flags & ~(BPF_SK_LOOKUP_F_REPLACE |
+                              BPF_SK_LOOKUP_F_NO_REUSEPORT)))
+               return -EINVAL;
+       if (unlikely(sk && sk_is_refcounted(sk)))
+               return -ESOCKTNOSUPPORT; /* reject non-RCU freed sockets */
+       if (unlikely(sk && sk->sk_state == TCP_ESTABLISHED))
+               return -ESOCKTNOSUPPORT; /* reject connected sockets */
+
+       /* Check if socket is suitable for packet L3/L4 protocol */
+       if (sk && sk->sk_protocol != ctx->protocol)
+               return -EPROTOTYPE;
+       if (sk && sk->sk_family != ctx->family &&
+           (sk->sk_family == AF_INET || ipv6_only_sock(sk)))
+               return -EAFNOSUPPORT;
+
+       if (ctx->selected_sk && !(flags & BPF_SK_LOOKUP_F_REPLACE))
+               return -EEXIST;
+
+       /* Select socket as lookup result */
+       ctx->selected_sk = sk;
+       ctx->no_reuseport = flags & BPF_SK_LOOKUP_F_NO_REUSEPORT;
+       return 0;
 }
 
-/* Define a list of socket types which can be the argument for
- * skc_to_*_sock() helpers. All these sockets should have
- * sock_common as the first argument in its memory layout.
- */
-#define BTF_SOCK_TYPE_xxx \
-       BTF_SOCK_TYPE(BTF_SOCK_TYPE_INET, "inet_sock")                  \
-       BTF_SOCK_TYPE(BTF_SOCK_TYPE_INET_CONN, "inet_connection_sock")  \
-       BTF_SOCK_TYPE(BTF_SOCK_TYPE_INET_REQ, "inet_request_sock")      \
-       BTF_SOCK_TYPE(BTF_SOCK_TYPE_INET_TW, "inet_timewait_sock")      \
-       BTF_SOCK_TYPE(BTF_SOCK_TYPE_REQ, "request_sock")                \
-       BTF_SOCK_TYPE(BTF_SOCK_TYPE_SOCK, "sock")                       \
-       BTF_SOCK_TYPE(BTF_SOCK_TYPE_SOCK_COMMON, "sock_common")         \
-       BTF_SOCK_TYPE(BTF_SOCK_TYPE_TCP, "tcp_sock")                    \
-       BTF_SOCK_TYPE(BTF_SOCK_TYPE_TCP_REQ, "tcp_request_sock")        \
-       BTF_SOCK_TYPE(BTF_SOCK_TYPE_TCP_TW, "tcp_timewait_sock")        \
-       BTF_SOCK_TYPE(BTF_SOCK_TYPE_TCP6, "tcp6_sock")                  \
-       BTF_SOCK_TYPE(BTF_SOCK_TYPE_UDP, "udp_sock")                    \
-       BTF_SOCK_TYPE(BTF_SOCK_TYPE_UDP6, "udp6_sock")
-
-enum {
-#define BTF_SOCK_TYPE(name, str) name,
-BTF_SOCK_TYPE_xxx
-#undef BTF_SOCK_TYPE
-MAX_BTF_SOCK_TYPE,
+static const struct bpf_func_proto bpf_sk_lookup_assign_proto = {
+       .func           = bpf_sk_lookup_assign,
+       .gpl_only       = false,
+       .ret_type       = RET_INTEGER,
+       .arg1_type      = ARG_PTR_TO_CTX,
+       .arg2_type      = ARG_PTR_TO_SOCKET_OR_NULL,
+       .arg3_type      = ARG_ANYTHING,
 };
 
-static int btf_sock_ids[MAX_BTF_SOCK_TYPE];
+static const struct bpf_func_proto *
+sk_lookup_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
+{
+       switch (func_id) {
+       case BPF_FUNC_perf_event_output:
+               return &bpf_event_output_data_proto;
+       case BPF_FUNC_sk_assign:
+               return &bpf_sk_lookup_assign_proto;
+       case BPF_FUNC_sk_release:
+               return &bpf_sk_release_proto;
+       default:
+               return bpf_base_func_proto(func_id);
+       }
+}
 
-#ifdef CONFIG_BPF_SYSCALL
-static const char *bpf_sock_types[] = {
-#define BTF_SOCK_TYPE(name, str) str,
-BTF_SOCK_TYPE_xxx
-#undef BTF_SOCK_TYPE
-};
+static bool sk_lookup_is_valid_access(int off, int size,
+                                     enum bpf_access_type type,
+                                     const struct bpf_prog *prog,
+                                     struct bpf_insn_access_aux *info)
+{
+       if (off < 0 || off >= sizeof(struct bpf_sk_lookup))
+               return false;
+       if (off % size != 0)
+               return false;
+       if (type != BPF_READ)
+               return false;
+
+       switch (off) {
+       case offsetof(struct bpf_sk_lookup, sk):
+               info->reg_type = PTR_TO_SOCKET_OR_NULL;
+               return size == sizeof(__u64);
 
-void init_btf_sock_ids(struct btf *btf)
+       case bpf_ctx_range(struct bpf_sk_lookup, family):
+       case bpf_ctx_range(struct bpf_sk_lookup, protocol):
+       case bpf_ctx_range(struct bpf_sk_lookup, remote_ip4):
+       case bpf_ctx_range(struct bpf_sk_lookup, local_ip4):
+       case bpf_ctx_range_till(struct bpf_sk_lookup, remote_ip6[0], remote_ip6[3]):
+       case bpf_ctx_range_till(struct bpf_sk_lookup, local_ip6[0], local_ip6[3]):
+       case bpf_ctx_range(struct bpf_sk_lookup, remote_port):
+       case bpf_ctx_range(struct bpf_sk_lookup, local_port):
+               bpf_ctx_record_field_size(info, sizeof(__u32));
+               return bpf_ctx_narrow_access_ok(off, size, sizeof(__u32));
+
+       default:
+               return false;
+       }
+}
+
+static u32 sk_lookup_convert_ctx_access(enum bpf_access_type type,
+                                       const struct bpf_insn *si,
+                                       struct bpf_insn *insn_buf,
+                                       struct bpf_prog *prog,
+                                       u32 *target_size)
 {
-       int i, btf_id;
+       struct bpf_insn *insn = insn_buf;
+
+       switch (si->off) {
+       case offsetof(struct bpf_sk_lookup, sk):
+               *insn++ = BPF_LDX_MEM(BPF_SIZEOF(void *), si->dst_reg, si->src_reg,
+                                     offsetof(struct bpf_sk_lookup_kern, selected_sk));
+               break;
 
-       for (i = 0; i < MAX_BTF_SOCK_TYPE; i++) {
-               btf_id = btf_find_by_name_kind(btf, bpf_sock_types[i],
-                                              BTF_KIND_STRUCT);
-               if (btf_id > 0)
-                       btf_sock_ids[i] = btf_id;
+       case offsetof(struct bpf_sk_lookup, family):
+               *insn++ = BPF_LDX_MEM(BPF_H, si->dst_reg, si->src_reg,
+                                     bpf_target_off(struct bpf_sk_lookup_kern,
+                                                    family, 2, target_size));
+               break;
+
+       case offsetof(struct bpf_sk_lookup, protocol):
+               *insn++ = BPF_LDX_MEM(BPF_H, si->dst_reg, si->src_reg,
+                                     bpf_target_off(struct bpf_sk_lookup_kern,
+                                                    protocol, 2, target_size));
+               break;
+
+       case offsetof(struct bpf_sk_lookup, remote_ip4):
+               *insn++ = BPF_LDX_MEM(BPF_W, si->dst_reg, si->src_reg,
+                                     bpf_target_off(struct bpf_sk_lookup_kern,
+                                                    v4.saddr, 4, target_size));
+               break;
+
+       case offsetof(struct bpf_sk_lookup, local_ip4):
+               *insn++ = BPF_LDX_MEM(BPF_W, si->dst_reg, si->src_reg,
+                                     bpf_target_off(struct bpf_sk_lookup_kern,
+                                                    v4.daddr, 4, target_size));
+               break;
+
+       case bpf_ctx_range_till(struct bpf_sk_lookup,
+                               remote_ip6[0], remote_ip6[3]): {
+#if IS_ENABLED(CONFIG_IPV6)
+               int off = si->off;
+
+               off -= offsetof(struct bpf_sk_lookup, remote_ip6[0]);
+               off += bpf_target_off(struct in6_addr, s6_addr32[0], 4, target_size);
+               *insn++ = BPF_LDX_MEM(BPF_SIZEOF(void *), si->dst_reg, si->src_reg,
+                                     offsetof(struct bpf_sk_lookup_kern, v6.saddr));
+               *insn++ = BPF_JMP_IMM(BPF_JEQ, si->dst_reg, 0, 1);
+               *insn++ = BPF_LDX_MEM(BPF_W, si->dst_reg, si->dst_reg, off);
+#else
+               *insn++ = BPF_MOV32_IMM(si->dst_reg, 0);
+#endif
+               break;
+       }
+       case bpf_ctx_range_till(struct bpf_sk_lookup,
+                               local_ip6[0], local_ip6[3]): {
+#if IS_ENABLED(CONFIG_IPV6)
+               int off = si->off;
+
+               off -= offsetof(struct bpf_sk_lookup, local_ip6[0]);
+               off += bpf_target_off(struct in6_addr, s6_addr32[0], 4, target_size);
+               *insn++ = BPF_LDX_MEM(BPF_SIZEOF(void *), si->dst_reg, si->src_reg,
+                                     offsetof(struct bpf_sk_lookup_kern, v6.daddr));
+               *insn++ = BPF_JMP_IMM(BPF_JEQ, si->dst_reg, 0, 1);
+               *insn++ = BPF_LDX_MEM(BPF_W, si->dst_reg, si->dst_reg, off);
+#else
+               *insn++ = BPF_MOV32_IMM(si->dst_reg, 0);
+#endif
+               break;
        }
+       case offsetof(struct bpf_sk_lookup, remote_port):
+               *insn++ = BPF_LDX_MEM(BPF_H, si->dst_reg, si->src_reg,
+                                     bpf_target_off(struct bpf_sk_lookup_kern,
+                                                    sport, 2, target_size));
+               break;
+
+       case offsetof(struct bpf_sk_lookup, local_port):
+               *insn++ = BPF_LDX_MEM(BPF_H, si->dst_reg, si->src_reg,
+                                     bpf_target_off(struct bpf_sk_lookup_kern,
+                                                    dport, 2, target_size));
+               break;
+       }
+
+       return insn - insn_buf;
 }
+
+const struct bpf_prog_ops sk_lookup_prog_ops = {
+};
+
+const struct bpf_verifier_ops sk_lookup_verifier_ops = {
+       .get_func_proto         = sk_lookup_func_proto,
+       .is_valid_access        = sk_lookup_is_valid_access,
+       .convert_ctx_access     = sk_lookup_convert_ctx_access,
+};
+
+#endif /* CONFIG_INET */
+
+DEFINE_BPF_DISPATCHER(xdp)
+
+void bpf_prog_change_xdp(struct bpf_prog *prev_prog, struct bpf_prog *prog)
+{
+       bpf_dispatcher_change_prog(BPF_DISPATCHER_PTR(xdp), prev_prog, prog);
+}
+
+#ifdef CONFIG_DEBUG_INFO_BTF
+BTF_ID_LIST_GLOBAL(btf_sock_ids)
+#define BTF_SOCK_TYPE(name, type) BTF_ID(struct, type)
+BTF_SOCK_TYPE_xxx
+#undef BTF_SOCK_TYPE
+#else
+u32 btf_sock_ids[MAX_BTF_SOCK_TYPE];
 #endif
 
 static bool check_arg_btf_id(u32 btf_id, u32 arg)