xref: /openbmc/linux/net/netfilter/nf_bpf_link.c (revision 83e98637)
184601d6eSFlorian Westphal // SPDX-License-Identifier: GPL-2.0
284601d6eSFlorian Westphal #include <linux/bpf.h>
3fd9c663bSFlorian Westphal #include <linux/filter.h>
491721c2dSDaniel Xu #include <linux/kmod.h>
591721c2dSDaniel Xu #include <linux/module.h>
684601d6eSFlorian Westphal #include <linux/netfilter.h>
784601d6eSFlorian Westphal 
884601d6eSFlorian Westphal #include <net/netfilter/nf_bpf_link.h>
984601d6eSFlorian Westphal #include <uapi/linux/netfilter_ipv4.h>
1084601d6eSFlorian Westphal 
nf_hook_run_bpf(void * bpf_prog,struct sk_buff * skb,const struct nf_hook_state * s)1184601d6eSFlorian Westphal static unsigned int nf_hook_run_bpf(void *bpf_prog, struct sk_buff *skb,
1284601d6eSFlorian Westphal 				    const struct nf_hook_state *s)
1384601d6eSFlorian Westphal {
14fd9c663bSFlorian Westphal 	const struct bpf_prog *prog = bpf_prog;
15fd9c663bSFlorian Westphal 	struct bpf_nf_ctx ctx = {
16fd9c663bSFlorian Westphal 		.state = s,
17fd9c663bSFlorian Westphal 		.skb = skb,
18fd9c663bSFlorian Westphal 	};
19fd9c663bSFlorian Westphal 
20fd9c663bSFlorian Westphal 	return bpf_prog_run(prog, &ctx);
2184601d6eSFlorian Westphal }
2284601d6eSFlorian Westphal 
2384601d6eSFlorian Westphal struct bpf_nf_link {
2484601d6eSFlorian Westphal 	struct bpf_link link;
2584601d6eSFlorian Westphal 	struct nf_hook_ops hook_ops;
2684601d6eSFlorian Westphal 	struct net *net;
2784601d6eSFlorian Westphal 	u32 dead;
2891721c2dSDaniel Xu 	const struct nf_defrag_hook *defrag_hook;
2984601d6eSFlorian Westphal };
3084601d6eSFlorian Westphal 
3181584c23SDaniel Xu #if IS_ENABLED(CONFIG_NF_DEFRAG_IPV4) || IS_ENABLED(CONFIG_NF_DEFRAG_IPV6)
3291721c2dSDaniel Xu static const struct nf_defrag_hook *
get_proto_defrag_hook(struct bpf_nf_link * link,const struct nf_defrag_hook __rcu ** ptr_global_hook,const char * mod)3391721c2dSDaniel Xu get_proto_defrag_hook(struct bpf_nf_link *link,
34*83e98637SD. Wythe 		      const struct nf_defrag_hook __rcu **ptr_global_hook,
3591721c2dSDaniel Xu 		      const char *mod)
3691721c2dSDaniel Xu {
3791721c2dSDaniel Xu 	const struct nf_defrag_hook *hook;
3891721c2dSDaniel Xu 	int err;
3991721c2dSDaniel Xu 
4091721c2dSDaniel Xu 	/* RCU protects us from races against module unloading */
4191721c2dSDaniel Xu 	rcu_read_lock();
42*83e98637SD. Wythe 	hook = rcu_dereference(*ptr_global_hook);
4391721c2dSDaniel Xu 	if (!hook) {
4491721c2dSDaniel Xu 		rcu_read_unlock();
4591721c2dSDaniel Xu 		err = request_module(mod);
4691721c2dSDaniel Xu 		if (err)
4791721c2dSDaniel Xu 			return ERR_PTR(err < 0 ? err : -EINVAL);
4891721c2dSDaniel Xu 
4991721c2dSDaniel Xu 		rcu_read_lock();
50*83e98637SD. Wythe 		hook = rcu_dereference(*ptr_global_hook);
5191721c2dSDaniel Xu 	}
5291721c2dSDaniel Xu 
5391721c2dSDaniel Xu 	if (hook && try_module_get(hook->owner)) {
5491721c2dSDaniel Xu 		/* Once we have a refcnt on the module, we no longer need RCU */
5591721c2dSDaniel Xu 		hook = rcu_pointer_handoff(hook);
5691721c2dSDaniel Xu 	} else {
5791721c2dSDaniel Xu 		WARN_ONCE(!hook, "%s has bad registration", mod);
5891721c2dSDaniel Xu 		hook = ERR_PTR(-ENOENT);
5991721c2dSDaniel Xu 	}
6091721c2dSDaniel Xu 	rcu_read_unlock();
6191721c2dSDaniel Xu 
6291721c2dSDaniel Xu 	if (!IS_ERR(hook)) {
6391721c2dSDaniel Xu 		err = hook->enable(link->net);
6491721c2dSDaniel Xu 		if (err) {
6591721c2dSDaniel Xu 			module_put(hook->owner);
6691721c2dSDaniel Xu 			hook = ERR_PTR(err);
6791721c2dSDaniel Xu 		}
6891721c2dSDaniel Xu 	}
6991721c2dSDaniel Xu 
7091721c2dSDaniel Xu 	return hook;
7191721c2dSDaniel Xu }
7281584c23SDaniel Xu #endif
7391721c2dSDaniel Xu 
bpf_nf_enable_defrag(struct bpf_nf_link * link)7491721c2dSDaniel Xu static int bpf_nf_enable_defrag(struct bpf_nf_link *link)
7591721c2dSDaniel Xu {
7691721c2dSDaniel Xu 	const struct nf_defrag_hook __maybe_unused *hook;
7791721c2dSDaniel Xu 
7891721c2dSDaniel Xu 	switch (link->hook_ops.pf) {
7991721c2dSDaniel Xu #if IS_ENABLED(CONFIG_NF_DEFRAG_IPV4)
8091721c2dSDaniel Xu 	case NFPROTO_IPV4:
81*83e98637SD. Wythe 		hook = get_proto_defrag_hook(link, &nf_defrag_v4_hook, "nf_defrag_ipv4");
8291721c2dSDaniel Xu 		if (IS_ERR(hook))
8391721c2dSDaniel Xu 			return PTR_ERR(hook);
8491721c2dSDaniel Xu 
8591721c2dSDaniel Xu 		link->defrag_hook = hook;
8691721c2dSDaniel Xu 		return 0;
8791721c2dSDaniel Xu #endif
8891721c2dSDaniel Xu #if IS_ENABLED(CONFIG_NF_DEFRAG_IPV6)
8991721c2dSDaniel Xu 	case NFPROTO_IPV6:
90*83e98637SD. Wythe 		hook = get_proto_defrag_hook(link, &nf_defrag_v6_hook, "nf_defrag_ipv6");
9191721c2dSDaniel Xu 		if (IS_ERR(hook))
9291721c2dSDaniel Xu 			return PTR_ERR(hook);
9391721c2dSDaniel Xu 
9491721c2dSDaniel Xu 		link->defrag_hook = hook;
9591721c2dSDaniel Xu 		return 0;
9691721c2dSDaniel Xu #endif
9791721c2dSDaniel Xu 	default:
9891721c2dSDaniel Xu 		return -EAFNOSUPPORT;
9991721c2dSDaniel Xu 	}
10091721c2dSDaniel Xu }
10191721c2dSDaniel Xu 
bpf_nf_disable_defrag(struct bpf_nf_link * link)10291721c2dSDaniel Xu static void bpf_nf_disable_defrag(struct bpf_nf_link *link)
10391721c2dSDaniel Xu {
10491721c2dSDaniel Xu 	const struct nf_defrag_hook *hook = link->defrag_hook;
10591721c2dSDaniel Xu 
10691721c2dSDaniel Xu 	if (!hook)
10791721c2dSDaniel Xu 		return;
10891721c2dSDaniel Xu 	hook->disable(link->net);
10991721c2dSDaniel Xu 	module_put(hook->owner);
11091721c2dSDaniel Xu }
11191721c2dSDaniel Xu 
bpf_nf_link_release(struct bpf_link * link)11284601d6eSFlorian Westphal static void bpf_nf_link_release(struct bpf_link *link)
11384601d6eSFlorian Westphal {
11484601d6eSFlorian Westphal 	struct bpf_nf_link *nf_link = container_of(link, struct bpf_nf_link, link);
11584601d6eSFlorian Westphal 
11684601d6eSFlorian Westphal 	if (nf_link->dead)
11784601d6eSFlorian Westphal 		return;
11884601d6eSFlorian Westphal 
11991721c2dSDaniel Xu 	/* do not double release in case .detach was already called */
12091721c2dSDaniel Xu 	if (!cmpxchg(&nf_link->dead, 0, 1)) {
12184601d6eSFlorian Westphal 		nf_unregister_net_hook(nf_link->net, &nf_link->hook_ops);
12291721c2dSDaniel Xu 		bpf_nf_disable_defrag(nf_link);
12391721c2dSDaniel Xu 	}
12484601d6eSFlorian Westphal }
12584601d6eSFlorian Westphal 
bpf_nf_link_dealloc(struct bpf_link * link)12684601d6eSFlorian Westphal static void bpf_nf_link_dealloc(struct bpf_link *link)
12784601d6eSFlorian Westphal {
12884601d6eSFlorian Westphal 	struct bpf_nf_link *nf_link = container_of(link, struct bpf_nf_link, link);
12984601d6eSFlorian Westphal 
13084601d6eSFlorian Westphal 	kfree(nf_link);
13184601d6eSFlorian Westphal }
13284601d6eSFlorian Westphal 
bpf_nf_link_detach(struct bpf_link * link)13384601d6eSFlorian Westphal static int bpf_nf_link_detach(struct bpf_link *link)
13484601d6eSFlorian Westphal {
13584601d6eSFlorian Westphal 	bpf_nf_link_release(link);
13684601d6eSFlorian Westphal 	return 0;
13784601d6eSFlorian Westphal }
13884601d6eSFlorian Westphal 
bpf_nf_link_show_info(const struct bpf_link * link,struct seq_file * seq)13984601d6eSFlorian Westphal static void bpf_nf_link_show_info(const struct bpf_link *link,
14084601d6eSFlorian Westphal 				  struct seq_file *seq)
14184601d6eSFlorian Westphal {
14284601d6eSFlorian Westphal 	struct bpf_nf_link *nf_link = container_of(link, struct bpf_nf_link, link);
14384601d6eSFlorian Westphal 
14484601d6eSFlorian Westphal 	seq_printf(seq, "pf:\t%u\thooknum:\t%u\tprio:\t%d\n",
14584601d6eSFlorian Westphal 		   nf_link->hook_ops.pf, nf_link->hook_ops.hooknum,
14684601d6eSFlorian Westphal 		   nf_link->hook_ops.priority);
14784601d6eSFlorian Westphal }
14884601d6eSFlorian Westphal 
bpf_nf_link_fill_link_info(const struct bpf_link * link,struct bpf_link_info * info)14984601d6eSFlorian Westphal static int bpf_nf_link_fill_link_info(const struct bpf_link *link,
15084601d6eSFlorian Westphal 				      struct bpf_link_info *info)
15184601d6eSFlorian Westphal {
15284601d6eSFlorian Westphal 	struct bpf_nf_link *nf_link = container_of(link, struct bpf_nf_link, link);
15384601d6eSFlorian Westphal 
15484601d6eSFlorian Westphal 	info->netfilter.pf = nf_link->hook_ops.pf;
15584601d6eSFlorian Westphal 	info->netfilter.hooknum = nf_link->hook_ops.hooknum;
15684601d6eSFlorian Westphal 	info->netfilter.priority = nf_link->hook_ops.priority;
15784601d6eSFlorian Westphal 	info->netfilter.flags = 0;
15884601d6eSFlorian Westphal 
15984601d6eSFlorian Westphal 	return 0;
16084601d6eSFlorian Westphal }
16184601d6eSFlorian Westphal 
bpf_nf_link_update(struct bpf_link * link,struct bpf_prog * new_prog,struct bpf_prog * old_prog)16284601d6eSFlorian Westphal static int bpf_nf_link_update(struct bpf_link *link, struct bpf_prog *new_prog,
16384601d6eSFlorian Westphal 			      struct bpf_prog *old_prog)
16484601d6eSFlorian Westphal {
16584601d6eSFlorian Westphal 	return -EOPNOTSUPP;
16684601d6eSFlorian Westphal }
16784601d6eSFlorian Westphal 
16884601d6eSFlorian Westphal static const struct bpf_link_ops bpf_nf_link_lops = {
16984601d6eSFlorian Westphal 	.release = bpf_nf_link_release,
17084601d6eSFlorian Westphal 	.dealloc = bpf_nf_link_dealloc,
17184601d6eSFlorian Westphal 	.detach = bpf_nf_link_detach,
17284601d6eSFlorian Westphal 	.show_fdinfo = bpf_nf_link_show_info,
17384601d6eSFlorian Westphal 	.fill_link_info = bpf_nf_link_fill_link_info,
17484601d6eSFlorian Westphal 	.update_prog = bpf_nf_link_update,
17584601d6eSFlorian Westphal };
17684601d6eSFlorian Westphal 
bpf_nf_check_pf_and_hooks(const union bpf_attr * attr)17784601d6eSFlorian Westphal static int bpf_nf_check_pf_and_hooks(const union bpf_attr *attr)
17884601d6eSFlorian Westphal {
17991721c2dSDaniel Xu 	int prio;
18091721c2dSDaniel Xu 
18184601d6eSFlorian Westphal 	switch (attr->link_create.netfilter.pf) {
18284601d6eSFlorian Westphal 	case NFPROTO_IPV4:
18384601d6eSFlorian Westphal 	case NFPROTO_IPV6:
18484601d6eSFlorian Westphal 		if (attr->link_create.netfilter.hooknum >= NF_INET_NUMHOOKS)
18584601d6eSFlorian Westphal 			return -EPROTO;
18684601d6eSFlorian Westphal 		break;
18784601d6eSFlorian Westphal 	default:
18884601d6eSFlorian Westphal 		return -EAFNOSUPPORT;
18984601d6eSFlorian Westphal 	}
19084601d6eSFlorian Westphal 
19191721c2dSDaniel Xu 	if (attr->link_create.netfilter.flags & ~BPF_F_NETFILTER_IP_DEFRAG)
19284601d6eSFlorian Westphal 		return -EOPNOTSUPP;
19384601d6eSFlorian Westphal 
19491721c2dSDaniel Xu 	/* make sure conntrack confirm is always last */
19591721c2dSDaniel Xu 	prio = attr->link_create.netfilter.priority;
19691721c2dSDaniel Xu 	if (prio == NF_IP_PRI_FIRST)
19791721c2dSDaniel Xu 		return -ERANGE;  /* sabotage_in and other warts */
19891721c2dSDaniel Xu 	else if (prio == NF_IP_PRI_LAST)
19991721c2dSDaniel Xu 		return -ERANGE;  /* e.g. conntrack confirm */
20091721c2dSDaniel Xu 	else if ((attr->link_create.netfilter.flags & BPF_F_NETFILTER_IP_DEFRAG) &&
20191721c2dSDaniel Xu 		 prio <= NF_IP_PRI_CONNTRACK_DEFRAG)
20291721c2dSDaniel Xu 		return -ERANGE;  /* cannot use defrag if prog runs before nf_defrag */
20384601d6eSFlorian Westphal 
20484601d6eSFlorian Westphal 	return 0;
20584601d6eSFlorian Westphal }
20684601d6eSFlorian Westphal 
bpf_nf_link_attach(const union bpf_attr * attr,struct bpf_prog * prog)20784601d6eSFlorian Westphal int bpf_nf_link_attach(const union bpf_attr *attr, struct bpf_prog *prog)
20884601d6eSFlorian Westphal {
20984601d6eSFlorian Westphal 	struct net *net = current->nsproxy->net_ns;
21084601d6eSFlorian Westphal 	struct bpf_link_primer link_primer;
21184601d6eSFlorian Westphal 	struct bpf_nf_link *link;
21284601d6eSFlorian Westphal 	int err;
21384601d6eSFlorian Westphal 
21484601d6eSFlorian Westphal 	if (attr->link_create.flags)
21584601d6eSFlorian Westphal 		return -EINVAL;
21684601d6eSFlorian Westphal 
21784601d6eSFlorian Westphal 	err = bpf_nf_check_pf_and_hooks(attr);
21884601d6eSFlorian Westphal 	if (err)
21984601d6eSFlorian Westphal 		return err;
22084601d6eSFlorian Westphal 
22184601d6eSFlorian Westphal 	link = kzalloc(sizeof(*link), GFP_USER);
22284601d6eSFlorian Westphal 	if (!link)
22384601d6eSFlorian Westphal 		return -ENOMEM;
22484601d6eSFlorian Westphal 
22584601d6eSFlorian Westphal 	bpf_link_init(&link->link, BPF_LINK_TYPE_NETFILTER, &bpf_nf_link_lops, prog);
22684601d6eSFlorian Westphal 
22784601d6eSFlorian Westphal 	link->hook_ops.hook = nf_hook_run_bpf;
22884601d6eSFlorian Westphal 	link->hook_ops.hook_ops_type = NF_HOOK_OP_BPF;
22984601d6eSFlorian Westphal 	link->hook_ops.priv = prog;
23084601d6eSFlorian Westphal 
23184601d6eSFlorian Westphal 	link->hook_ops.pf = attr->link_create.netfilter.pf;
23284601d6eSFlorian Westphal 	link->hook_ops.priority = attr->link_create.netfilter.priority;
23384601d6eSFlorian Westphal 	link->hook_ops.hooknum = attr->link_create.netfilter.hooknum;
23484601d6eSFlorian Westphal 
23584601d6eSFlorian Westphal 	link->net = net;
23684601d6eSFlorian Westphal 	link->dead = false;
23791721c2dSDaniel Xu 	link->defrag_hook = NULL;
23884601d6eSFlorian Westphal 
23984601d6eSFlorian Westphal 	err = bpf_link_prime(&link->link, &link_primer);
24084601d6eSFlorian Westphal 	if (err) {
24184601d6eSFlorian Westphal 		kfree(link);
24284601d6eSFlorian Westphal 		return err;
24384601d6eSFlorian Westphal 	}
24484601d6eSFlorian Westphal 
24591721c2dSDaniel Xu 	if (attr->link_create.netfilter.flags & BPF_F_NETFILTER_IP_DEFRAG) {
24691721c2dSDaniel Xu 		err = bpf_nf_enable_defrag(link);
24791721c2dSDaniel Xu 		if (err) {
24891721c2dSDaniel Xu 			bpf_link_cleanup(&link_primer);
24991721c2dSDaniel Xu 			return err;
25091721c2dSDaniel Xu 		}
25191721c2dSDaniel Xu 	}
25291721c2dSDaniel Xu 
25384601d6eSFlorian Westphal 	err = nf_register_net_hook(net, &link->hook_ops);
25484601d6eSFlorian Westphal 	if (err) {
25591721c2dSDaniel Xu 		bpf_nf_disable_defrag(link);
25684601d6eSFlorian Westphal 		bpf_link_cleanup(&link_primer);
25784601d6eSFlorian Westphal 		return err;
25884601d6eSFlorian Westphal 	}
25984601d6eSFlorian Westphal 
26084601d6eSFlorian Westphal 	return bpf_link_settle(&link_primer);
26184601d6eSFlorian Westphal }
262fd9c663bSFlorian Westphal 
263fd9c663bSFlorian Westphal const struct bpf_prog_ops netfilter_prog_ops = {
2642b99ef22SFlorian Westphal 	.test_run = bpf_prog_test_run_nf,
265fd9c663bSFlorian Westphal };
266fd9c663bSFlorian Westphal 
nf_ptr_to_btf_id(struct bpf_insn_access_aux * info,const char * name)267fd9c663bSFlorian Westphal static bool nf_ptr_to_btf_id(struct bpf_insn_access_aux *info, const char *name)
268fd9c663bSFlorian Westphal {
269fd9c663bSFlorian Westphal 	struct btf *btf;
270fd9c663bSFlorian Westphal 	s32 type_id;
271fd9c663bSFlorian Westphal 
272fd9c663bSFlorian Westphal 	btf = bpf_get_btf_vmlinux();
273fd9c663bSFlorian Westphal 	if (IS_ERR_OR_NULL(btf))
274fd9c663bSFlorian Westphal 		return false;
275fd9c663bSFlorian Westphal 
276fd9c663bSFlorian Westphal 	type_id = btf_find_by_name_kind(btf, name, BTF_KIND_STRUCT);
277fd9c663bSFlorian Westphal 	if (WARN_ON_ONCE(type_id < 0))
278fd9c663bSFlorian Westphal 		return false;
279fd9c663bSFlorian Westphal 
280fd9c663bSFlorian Westphal 	info->btf = btf;
281fd9c663bSFlorian Westphal 	info->btf_id = type_id;
282fd9c663bSFlorian Westphal 	info->reg_type = PTR_TO_BTF_ID | PTR_TRUSTED;
283fd9c663bSFlorian Westphal 	return true;
284fd9c663bSFlorian Westphal }
285fd9c663bSFlorian Westphal 
nf_is_valid_access(int off,int size,enum bpf_access_type type,const struct bpf_prog * prog,struct bpf_insn_access_aux * info)286fd9c663bSFlorian Westphal static bool nf_is_valid_access(int off, int size, enum bpf_access_type type,
287fd9c663bSFlorian Westphal 			       const struct bpf_prog *prog,
288fd9c663bSFlorian Westphal 			       struct bpf_insn_access_aux *info)
289fd9c663bSFlorian Westphal {
290fd9c663bSFlorian Westphal 	if (off < 0 || off >= sizeof(struct bpf_nf_ctx))
291fd9c663bSFlorian Westphal 		return false;
292fd9c663bSFlorian Westphal 
293fd9c663bSFlorian Westphal 	if (type == BPF_WRITE)
294fd9c663bSFlorian Westphal 		return false;
295fd9c663bSFlorian Westphal 
296fd9c663bSFlorian Westphal 	switch (off) {
297fd9c663bSFlorian Westphal 	case bpf_ctx_range(struct bpf_nf_ctx, skb):
298fd9c663bSFlorian Westphal 		if (size != sizeof_field(struct bpf_nf_ctx, skb))
299fd9c663bSFlorian Westphal 			return false;
300fd9c663bSFlorian Westphal 
301fd9c663bSFlorian Westphal 		return nf_ptr_to_btf_id(info, "sk_buff");
302fd9c663bSFlorian Westphal 	case bpf_ctx_range(struct bpf_nf_ctx, state):
303fd9c663bSFlorian Westphal 		if (size != sizeof_field(struct bpf_nf_ctx, state))
304fd9c663bSFlorian Westphal 			return false;
305fd9c663bSFlorian Westphal 
306fd9c663bSFlorian Westphal 		return nf_ptr_to_btf_id(info, "nf_hook_state");
307fd9c663bSFlorian Westphal 	default:
308fd9c663bSFlorian Westphal 		return false;
309fd9c663bSFlorian Westphal 	}
310fd9c663bSFlorian Westphal 
311fd9c663bSFlorian Westphal 	return false;
312fd9c663bSFlorian Westphal }
313fd9c663bSFlorian Westphal 
314fd9c663bSFlorian Westphal static const struct bpf_func_proto *
bpf_nf_func_proto(enum bpf_func_id func_id,const struct bpf_prog * prog)315fd9c663bSFlorian Westphal bpf_nf_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
316fd9c663bSFlorian Westphal {
317fd9c663bSFlorian Westphal 	return bpf_base_func_proto(func_id);
318fd9c663bSFlorian Westphal }
319fd9c663bSFlorian Westphal 
320fd9c663bSFlorian Westphal const struct bpf_verifier_ops netfilter_verifier_ops = {
321fd9c663bSFlorian Westphal 	.is_valid_access	= nf_is_valid_access,
322fd9c663bSFlorian Westphal 	.get_func_proto		= bpf_nf_func_proto,
323fd9c663bSFlorian Westphal };
324