@@ -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 *)
@@ -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);
@@ -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"
@@ -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.
@@ -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;
+}
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(-)