Skip to content

Commit

Permalink
bpf: minimal support for programs hooked into netfilter framework
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Florian Westphal authored and intel-lab-lkp committed Feb 8, 2023
1 parent 27ac276 commit 594e7d0
Show file tree
Hide file tree
Showing 5 changed files with 145 additions and 1 deletion.
4 changes: 4 additions & 0 deletions include/linux/bpf_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 *)

Expand Down
8 changes: 8 additions & 0 deletions include/net/netfilter/nf_hook_bpf.h
Original file line number Diff line number Diff line change
@@ -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);
3 changes: 3 additions & 0 deletions kernel/bpf/btf.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down
3 changes: 3 additions & 0 deletions kernel/bpf/verifier.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
128 changes: 127 additions & 1 deletion net/netfilter/nf_bpf_link.c
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down Expand Up @@ -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;
}

0 comments on commit 594e7d0

Please sign in to comment.