Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

bpf: minimal support for programs hooked into netfilter framework

This adds minimal support for BPF_PROG_TYPE_NETFILTER bpf programs
that will be invoked via the NF_HOOK() points in the ip 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 restricts verdicts to either DROP or ACCEPT.

Signed-off-by: Florian Westphal <fw@strlen.de>
Link: https://lore.kernel.org/r/20230421170300.24115-3-fw@strlen.de
Signed-off-by: Alexei Starovoitov <ast@kernel.org>

authored by

Florian Westphal and committed by
Alexei Starovoitov
fd9c663b 84601d6e

+88 -1
+4
include/linux/bpf_types.h
··· 79 79 #endif 80 80 BPF_PROG_TYPE(BPF_PROG_TYPE_SYSCALL, bpf_syscall, 81 81 void *, void *) 82 + #ifdef CONFIG_NETFILTER 83 + BPF_PROG_TYPE(BPF_PROG_TYPE_NETFILTER, netfilter, 84 + struct bpf_nf_ctx, struct bpf_nf_ctx) 85 + #endif 82 86 83 87 BPF_MAP_TYPE(BPF_MAP_TYPE_ARRAY, array_map_ops) 84 88 BPF_MAP_TYPE(BPF_MAP_TYPE_PERCPU_ARRAY, percpu_array_map_ops)
+5
include/net/netfilter/nf_bpf_link.h
··· 1 1 /* SPDX-License-Identifier: GPL-2.0 */ 2 2 3 + struct bpf_nf_ctx { 4 + const struct nf_hook_state *state; 5 + struct sk_buff *skb; 6 + }; 7 + 3 8 #if IS_ENABLED(CONFIG_NETFILTER_BPF_LINK) 4 9 int bpf_nf_link_attach(const union bpf_attr *attr, struct bpf_prog *prog); 5 10 #else
+6
kernel/bpf/btf.c
··· 25 25 #include <linux/bsearch.h> 26 26 #include <linux/kobject.h> 27 27 #include <linux/sysfs.h> 28 + 29 + #include <net/netfilter/nf_bpf_link.h> 30 + 28 31 #include <net/sock.h> 29 32 #include "../tools/lib/bpf/relo_core.h" 30 33 ··· 215 212 BTF_KFUNC_HOOK_SK_SKB, 216 213 BTF_KFUNC_HOOK_SOCKET_FILTER, 217 214 BTF_KFUNC_HOOK_LWT, 215 + BTF_KFUNC_HOOK_NETFILTER, 218 216 BTF_KFUNC_HOOK_MAX, 219 217 }; 220 218 ··· 7806 7802 case BPF_PROG_TYPE_LWT_XMIT: 7807 7803 case BPF_PROG_TYPE_LWT_SEG6LOCAL: 7808 7804 return BTF_KFUNC_HOOK_LWT; 7805 + case BPF_PROG_TYPE_NETFILTER: 7806 + return BTF_KFUNC_HOOK_NETFILTER; 7809 7807 default: 7810 7808 return BTF_KFUNC_HOOK_MAX; 7811 7809 }
+3
kernel/bpf/verifier.c
··· 13816 13816 } 13817 13817 break; 13818 13818 13819 + case BPF_PROG_TYPE_NETFILTER: 13820 + range = tnum_range(NF_DROP, NF_ACCEPT); 13821 + break; 13819 13822 case BPF_PROG_TYPE_EXT: 13820 13823 /* freplace program can return anything as its return value 13821 13824 * depends on the to-be-replaced kernel func or bpf program.
+1
net/core/filter.c
··· 11717 11717 ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_LWT_IN, &bpf_kfunc_set_skb); 11718 11718 ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_LWT_XMIT, &bpf_kfunc_set_skb); 11719 11719 ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_LWT_SEG6LOCAL, &bpf_kfunc_set_skb); 11720 + ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_NETFILTER, &bpf_kfunc_set_skb); 11720 11721 return ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_XDP, &bpf_kfunc_set_xdp); 11721 11722 } 11722 11723 late_initcall(bpf_kfunc_init);
+69 -1
net/netfilter/nf_bpf_link.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0 2 2 #include <linux/bpf.h> 3 + #include <linux/filter.h> 3 4 #include <linux/netfilter.h> 4 5 5 6 #include <net/netfilter/nf_bpf_link.h> ··· 9 8 static unsigned int nf_hook_run_bpf(void *bpf_prog, struct sk_buff *skb, 10 9 const struct nf_hook_state *s) 11 10 { 12 - return NF_ACCEPT; 11 + const struct bpf_prog *prog = bpf_prog; 12 + struct bpf_nf_ctx ctx = { 13 + .state = s, 14 + .skb = skb, 15 + }; 16 + 17 + return bpf_prog_run(prog, &ctx); 13 18 } 14 19 15 20 struct bpf_nf_link { ··· 164 157 165 158 return bpf_link_settle(&link_primer); 166 159 } 160 + 161 + const struct bpf_prog_ops netfilter_prog_ops = { 162 + }; 163 + 164 + static bool nf_ptr_to_btf_id(struct bpf_insn_access_aux *info, const char *name) 165 + { 166 + struct btf *btf; 167 + s32 type_id; 168 + 169 + btf = bpf_get_btf_vmlinux(); 170 + if (IS_ERR_OR_NULL(btf)) 171 + return false; 172 + 173 + type_id = btf_find_by_name_kind(btf, name, BTF_KIND_STRUCT); 174 + if (WARN_ON_ONCE(type_id < 0)) 175 + return false; 176 + 177 + info->btf = btf; 178 + info->btf_id = type_id; 179 + info->reg_type = PTR_TO_BTF_ID | PTR_TRUSTED; 180 + return true; 181 + } 182 + 183 + static bool nf_is_valid_access(int off, int size, enum bpf_access_type type, 184 + const struct bpf_prog *prog, 185 + struct bpf_insn_access_aux *info) 186 + { 187 + if (off < 0 || off >= sizeof(struct bpf_nf_ctx)) 188 + return false; 189 + 190 + if (type == BPF_WRITE) 191 + return false; 192 + 193 + switch (off) { 194 + case bpf_ctx_range(struct bpf_nf_ctx, skb): 195 + if (size != sizeof_field(struct bpf_nf_ctx, skb)) 196 + return false; 197 + 198 + return nf_ptr_to_btf_id(info, "sk_buff"); 199 + case bpf_ctx_range(struct bpf_nf_ctx, state): 200 + if (size != sizeof_field(struct bpf_nf_ctx, state)) 201 + return false; 202 + 203 + return nf_ptr_to_btf_id(info, "nf_hook_state"); 204 + default: 205 + return false; 206 + } 207 + 208 + return false; 209 + } 210 + 211 + static const struct bpf_func_proto * 212 + bpf_nf_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) 213 + { 214 + return bpf_base_func_proto(func_id); 215 + } 216 + 217 + const struct bpf_verifier_ops netfilter_verifier_ops = { 218 + .is_valid_access = nf_is_valid_access, 219 + .get_func_proto = bpf_nf_func_proto, 220 + };