BPF List
 help / color / mirror / Atom feed
From: Florian Westphal <fw@strlen.de>
To: <bpf@vger.kernel.org>
Cc: <netfilter-devel@vger.kernel.org>, Florian Westphal <fw@strlen.de>
Subject: [RFC nf-next 3/3] bpf: minimal support for programs hooked into netfilter framework
Date: Wed,  8 Feb 2023 17:03:07 +0100	[thread overview]
Message-ID: <20230208160307.27534-4-fw@strlen.de> (raw)
In-Reply-To: <20230208160307.27534-1-fw@strlen.de>

Not for merging: has problems.

This adds minimal support for BPF_PROG_TYPE_NETFILTER bpf programs
that will be invoked via the NF_HOOK() points in the ip(6) stack.

Invocation incurs an indirect call.  This is not a necessity: Its
possible to add 'DEFINE_BPF_DISPATCHER(nf_progs)' and handle the
program invocation with the same method already done for xdp progs.

This isn't done here to keep the size of this chunk down.

Verifier will reject programs that don't return either DROP or ACCEPT
verdicts.

Programs currently pretend they have prototype

  func(struct __sk_buff *skb)

with rewrite via verifier, but this will be changed to native kernel struct, i.e.:

  func(struct bpf_nf_ctx *ctx)

Instead of direct packet access, plan is to have programs use upcoming
'dynptr' api.

For 'traditional' netfilter (c-functions), skb->data is only guaranteed
to be linear for the ip/ip6 header, for everything else
skb_header_pointer is mandatory.

Signed-off-by: Florian Westphal <fw@strlen.de>
---
 include/linux/bpf_types.h           |   4 +
 include/net/netfilter/nf_hook_bpf.h |   8 ++
 kernel/bpf/btf.c                    |   3 +
 kernel/bpf/verifier.c               |   3 +
 net/netfilter/nf_bpf_link.c         | 128 +++++++++++++++++++++++++++-
 5 files changed, 145 insertions(+), 1 deletion(-)

diff --git a/include/linux/bpf_types.h b/include/linux/bpf_types.h
index d4ee3ccd3753..ecc7444b31d4 100644
--- a/include/linux/bpf_types.h
+++ b/include/linux/bpf_types.h
@@ -77,6 +77,10 @@ BPF_PROG_TYPE(BPF_PROG_TYPE_LSM, lsm,
 	       void *, void *)
 #endif /* CONFIG_BPF_LSM */
 #endif
+#ifdef CONFIG_NETFILTER
+BPF_PROG_TYPE(BPF_PROG_TYPE_NETFILTER, netfilter,
+	      struct __sk_buff, struct bpf_nf_ctx)
+#endif
 BPF_PROG_TYPE(BPF_PROG_TYPE_SYSCALL, bpf_syscall,
 	      void *, void *)
 
diff --git a/include/net/netfilter/nf_hook_bpf.h b/include/net/netfilter/nf_hook_bpf.h
index 9d1b338e89d7..cf02e8394598 100644
--- a/include/net/netfilter/nf_hook_bpf.h
+++ b/include/net/netfilter/nf_hook_bpf.h
@@ -1,2 +1,10 @@
 /* SPDX-License-Identifier: GPL-2.0 */
+
+struct bpf_nf_ctx {
+	const struct nf_hook_state *state;
+	const struct sk_buff *skb;
+	const void *data;
+	const void *data_end;
+};
+
 int bpf_nf_link_attach(const union bpf_attr *attr, struct bpf_prog *prog);
diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
index 47b8cb96f2c2..07339c81d9f1 100644
--- a/kernel/bpf/btf.c
+++ b/kernel/bpf/btf.c
@@ -25,6 +25,9 @@
 #include <linux/bsearch.h>
 #include <linux/kobject.h>
 #include <linux/sysfs.h>
+
+#include <net/netfilter/nf_hook_bpf.h>
+
 #include <net/sock.h>
 #include "../tools/lib/bpf/relo_core.h"
 
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 0973660a30ec..e63ecbb69250 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -12547,6 +12547,9 @@ static int check_return_code(struct bpf_verifier_env *env)
 		}
 		break;
 
+	case BPF_PROG_TYPE_NETFILTER:
+		range = tnum_range(NF_DROP, NF_ACCEPT);
+		break;
 	case BPF_PROG_TYPE_EXT:
 		/* freplace program can return anything as its return value
 		 * depends on the to-be-replaced kernel func or bpf program.
diff --git a/net/netfilter/nf_bpf_link.c b/net/netfilter/nf_bpf_link.c
index fa4fae5cc669..98a6f7dc05e2 100644
--- a/net/netfilter/nf_bpf_link.c
+++ b/net/netfilter/nf_bpf_link.c
@@ -1,12 +1,21 @@
 // SPDX-License-Identifier: GPL-2.0
 #include <linux/bpf.h>
+#include <linux/filter.h>
 #include <linux/netfilter.h>
 
 #include <net/netfilter/nf_hook_bpf.h>
 
 static unsigned int nf_hook_run_bpf(void *bpf_prog, struct sk_buff *skb, const struct nf_hook_state *s)
 {
-	return NF_ACCEPT;
+	const struct bpf_prog *prog = bpf_prog;
+	struct bpf_nf_ctx ctx = {
+		.state = s,
+		.skb = skb,
+		.data = skb->data,
+		.data_end = skb->data + skb_headlen(skb),
+	};
+
+	return bpf_prog_run(prog, &ctx);
 }
 
 struct bpf_nf_link {
@@ -114,3 +123,120 @@ int bpf_nf_link_attach(const union bpf_attr *attr, struct bpf_prog *prog)
 	kfree(link);
 	return err;
 }
+
+static int bpf_prog_test_run_nf(struct bpf_prog *prog,
+				const union bpf_attr *kattr,
+				union bpf_attr __user *uattr)
+{
+	return -EOPNOTSUPP;
+}
+
+const struct bpf_prog_ops netfilter_prog_ops = {
+	.test_run		= bpf_prog_test_run_nf,
+};
+
+static u32 nf_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)
+{
+	struct bpf_insn *insn = insn_buf;
+
+	switch (si->off) {
+	case offsetof(struct __sk_buff, data):
+		*insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct bpf_nf_ctx, data),
+				      si->dst_reg, si->src_reg,
+				      offsetof(struct bpf_nf_ctx, data));
+		break;
+	case offsetof(struct __sk_buff, data_end):
+		*insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct bpf_nf_ctx, data_end),
+				      si->dst_reg, si->src_reg,
+				      offsetof(struct bpf_nf_ctx, data_end));
+		break;
+	}
+
+	return insn - insn_buf;
+}
+
+static bool nf_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 __sk_buff))
+		return false;
+
+	if (type == BPF_WRITE)
+		return false;
+
+	switch (off) {
+	case bpf_ctx_range(struct __sk_buff, data):
+		if (size != sizeof(u32))
+			return false;
+		info->reg_type = PTR_TO_PACKET;
+		return true;
+	case bpf_ctx_range(struct __sk_buff, data_end):
+		if (size != sizeof(u32))
+			return false;
+		info->reg_type = PTR_TO_PACKET_END;
+		return true;
+	default:
+		return false;
+	}
+
+	return false;
+}
+
+static const struct bpf_func_proto *
+bpf_nf_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
+{
+	return bpf_base_func_proto(func_id);
+}
+
+const struct bpf_verifier_ops netfilter_verifier_ops = {
+	.is_valid_access	= nf_is_valid_access,
+	.convert_ctx_access	= nf_convert_ctx_access,
+	.get_func_proto		= bpf_nf_func_proto,
+};
+
+__diag_push();
+__diag_ignore_all("-Wmissing-prototypes",
+		  "kfuncs which will be used in BPF programs");
+
+/* bpf_nf_hook_state_ctx_get - get nf_hook_state context structure
+ *
+ * Get the real nf_hook_state context structure.
+ *
+ *
+ */
+const struct nf_hook_state *bpf_nf_hook_state_ctx_get(struct __sk_buff *s)
+{
+	return (const struct nf_hook_state *)s;
+}
+
+int bpf_xt_change_status(struct nf_conn *nfct, u32 status)
+{
+	return 1;
+}
+
+__diag_pop()
+
+BTF_SET8_START(nf_hook_kfunc_set)
+BTF_ID_FLAGS(func, bpf_nf_hook_state_ctx_get, 0)
+BTF_ID_FLAGS(func, bpf_xt_change_status, KF_TRUSTED_ARGS)
+BTF_SET8_END(nf_hook_kfunc_set)
+
+static const struct btf_kfunc_id_set nf_basehook_kfunc_set = {
+	.owner = THIS_MODULE,
+	.set   = &nf_hook_kfunc_set,
+};
+
+int register_nf_hook_bpf(void)
+{
+	int ret;
+
+	ret = register_btf_kfunc_id_set(BPF_PROG_TYPE_NETFILTER, &nf_basehook_kfunc_set);
+	if (ret)
+		return ret;
+
+	return ret;
+}
-- 
2.39.1


      parent reply	other threads:[~2023-02-08 16:03 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-02-08 16:03 [RFC nf-next 0/3] bpf, netfilter: minimal support for bpf progs Florian Westphal
2023-02-08 16:03 ` [RFC nf-next 1/3] bpf: add bpf_link support for BPF_NETFILTER programs Florian Westphal
2023-02-08 16:03 ` [RFC nf-next 2/3] libbpf: sync header file, add nf prog section name Florian Westphal
2023-02-08 16:03 ` Florian Westphal [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20230208160307.27534-4-fw@strlen.de \
    --to=fw@strlen.de \
    --cc=bpf@vger.kernel.org \
    --cc=netfilter-devel@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox