xref: /openbmc/linux/kernel/bpf/net_namespace.c (revision 3b80b73a)
1b27f7bb5SJakub Sitnicki // SPDX-License-Identifier: GPL-2.0
2b27f7bb5SJakub Sitnicki 
3b27f7bb5SJakub Sitnicki #include <linux/bpf.h>
4*3b80b73aSJakub Kicinski #include <linux/bpf-netns.h>
5b27f7bb5SJakub Sitnicki #include <linux/filter.h>
6b27f7bb5SJakub Sitnicki #include <net/net_namespace.h>
7b27f7bb5SJakub Sitnicki 
8b27f7bb5SJakub Sitnicki /*
9b27f7bb5SJakub Sitnicki  * Functions to manage BPF programs attached to netns
10b27f7bb5SJakub Sitnicki  */
11b27f7bb5SJakub Sitnicki 
127f045a49SJakub Sitnicki struct bpf_netns_link {
137f045a49SJakub Sitnicki 	struct bpf_link	link;
147f045a49SJakub Sitnicki 	enum bpf_attach_type type;
157f045a49SJakub Sitnicki 	enum netns_bpf_attach_type netns_type;
167f045a49SJakub Sitnicki 
177f045a49SJakub Sitnicki 	/* We don't hold a ref to net in order to auto-detach the link
187f045a49SJakub Sitnicki 	 * when netns is going away. Instead we rely on pernet
197f045a49SJakub Sitnicki 	 * pre_exit callback to clear this pointer. Must be accessed
207f045a49SJakub Sitnicki 	 * with netns_bpf_mutex held.
217f045a49SJakub Sitnicki 	 */
227f045a49SJakub Sitnicki 	struct net *net;
23ab53cad9SJakub Sitnicki 	struct list_head node; /* node in list of links attached to net */
247f045a49SJakub Sitnicki };
257f045a49SJakub Sitnicki 
26b27f7bb5SJakub Sitnicki /* Protects updates to netns_bpf */
27b27f7bb5SJakub Sitnicki DEFINE_MUTEX(netns_bpf_mutex);
28b27f7bb5SJakub Sitnicki 
netns_bpf_attach_type_unneed(enum netns_bpf_attach_type type)291559b4aaSJakub Sitnicki static void netns_bpf_attach_type_unneed(enum netns_bpf_attach_type type)
301559b4aaSJakub Sitnicki {
311559b4aaSJakub Sitnicki 	switch (type) {
32343ead28SJakub Sitnicki #ifdef CONFIG_INET
331559b4aaSJakub Sitnicki 	case NETNS_BPF_SK_LOOKUP:
341559b4aaSJakub Sitnicki 		static_branch_dec(&bpf_sk_lookup_enabled);
351559b4aaSJakub Sitnicki 		break;
36343ead28SJakub Sitnicki #endif
371559b4aaSJakub Sitnicki 	default:
381559b4aaSJakub Sitnicki 		break;
391559b4aaSJakub Sitnicki 	}
401559b4aaSJakub Sitnicki }
411559b4aaSJakub Sitnicki 
netns_bpf_attach_type_need(enum netns_bpf_attach_type type)421559b4aaSJakub Sitnicki static void netns_bpf_attach_type_need(enum netns_bpf_attach_type type)
431559b4aaSJakub Sitnicki {
441559b4aaSJakub Sitnicki 	switch (type) {
45343ead28SJakub Sitnicki #ifdef CONFIG_INET
461559b4aaSJakub Sitnicki 	case NETNS_BPF_SK_LOOKUP:
471559b4aaSJakub Sitnicki 		static_branch_inc(&bpf_sk_lookup_enabled);
481559b4aaSJakub Sitnicki 		break;
49343ead28SJakub Sitnicki #endif
501559b4aaSJakub Sitnicki 	default:
511559b4aaSJakub Sitnicki 		break;
521559b4aaSJakub Sitnicki 	}
531559b4aaSJakub Sitnicki }
541559b4aaSJakub Sitnicki 
557f045a49SJakub Sitnicki /* Must be called with netns_bpf_mutex held. */
netns_bpf_run_array_detach(struct net * net,enum netns_bpf_attach_type type)56695c1214SJakub Sitnicki static void netns_bpf_run_array_detach(struct net *net,
57695c1214SJakub Sitnicki 				       enum netns_bpf_attach_type type)
58695c1214SJakub Sitnicki {
59695c1214SJakub Sitnicki 	struct bpf_prog_array *run_array;
60695c1214SJakub Sitnicki 
61695c1214SJakub Sitnicki 	run_array = rcu_replace_pointer(net->bpf.run_array[type], NULL,
62695c1214SJakub Sitnicki 					lockdep_is_held(&netns_bpf_mutex));
63695c1214SJakub Sitnicki 	bpf_prog_array_free(run_array);
64695c1214SJakub Sitnicki }
65695c1214SJakub Sitnicki 
link_index(struct net * net,enum netns_bpf_attach_type type,struct bpf_netns_link * link)66ce3aa9ccSJakub Sitnicki static int link_index(struct net *net, enum netns_bpf_attach_type type,
67ce3aa9ccSJakub Sitnicki 		      struct bpf_netns_link *link)
68ce3aa9ccSJakub Sitnicki {
69ce3aa9ccSJakub Sitnicki 	struct bpf_netns_link *pos;
70ce3aa9ccSJakub Sitnicki 	int i = 0;
71ce3aa9ccSJakub Sitnicki 
72ce3aa9ccSJakub Sitnicki 	list_for_each_entry(pos, &net->bpf.links[type], node) {
73ce3aa9ccSJakub Sitnicki 		if (pos == link)
74ce3aa9ccSJakub Sitnicki 			return i;
75ce3aa9ccSJakub Sitnicki 		i++;
76ce3aa9ccSJakub Sitnicki 	}
77ce3aa9ccSJakub Sitnicki 	return -ENOENT;
78ce3aa9ccSJakub Sitnicki }
79ce3aa9ccSJakub Sitnicki 
link_count(struct net * net,enum netns_bpf_attach_type type)80ce3aa9ccSJakub Sitnicki static int link_count(struct net *net, enum netns_bpf_attach_type type)
81ce3aa9ccSJakub Sitnicki {
82ce3aa9ccSJakub Sitnicki 	struct list_head *pos;
83ce3aa9ccSJakub Sitnicki 	int i = 0;
84ce3aa9ccSJakub Sitnicki 
85ce3aa9ccSJakub Sitnicki 	list_for_each(pos, &net->bpf.links[type])
86ce3aa9ccSJakub Sitnicki 		i++;
87ce3aa9ccSJakub Sitnicki 	return i;
88ce3aa9ccSJakub Sitnicki }
89ce3aa9ccSJakub Sitnicki 
fill_prog_array(struct net * net,enum netns_bpf_attach_type type,struct bpf_prog_array * prog_array)90ce3aa9ccSJakub Sitnicki static void fill_prog_array(struct net *net, enum netns_bpf_attach_type type,
91ce3aa9ccSJakub Sitnicki 			    struct bpf_prog_array *prog_array)
92ce3aa9ccSJakub Sitnicki {
93ce3aa9ccSJakub Sitnicki 	struct bpf_netns_link *pos;
94ce3aa9ccSJakub Sitnicki 	unsigned int i = 0;
95ce3aa9ccSJakub Sitnicki 
96ce3aa9ccSJakub Sitnicki 	list_for_each_entry(pos, &net->bpf.links[type], node) {
97ce3aa9ccSJakub Sitnicki 		prog_array->items[i].prog = pos->link.prog;
98ce3aa9ccSJakub Sitnicki 		i++;
99ce3aa9ccSJakub Sitnicki 	}
100ce3aa9ccSJakub Sitnicki }
101ce3aa9ccSJakub Sitnicki 
bpf_netns_link_release(struct bpf_link * link)1027f045a49SJakub Sitnicki static void bpf_netns_link_release(struct bpf_link *link)
1037f045a49SJakub Sitnicki {
1047f045a49SJakub Sitnicki 	struct bpf_netns_link *net_link =
1057f045a49SJakub Sitnicki 		container_of(link, struct bpf_netns_link, link);
1067f045a49SJakub Sitnicki 	enum netns_bpf_attach_type type = net_link->netns_type;
107ce3aa9ccSJakub Sitnicki 	struct bpf_prog_array *old_array, *new_array;
1087f045a49SJakub Sitnicki 	struct net *net;
109ce3aa9ccSJakub Sitnicki 	int cnt, idx;
1107f045a49SJakub Sitnicki 
1117f045a49SJakub Sitnicki 	mutex_lock(&netns_bpf_mutex);
1127f045a49SJakub Sitnicki 
1132576f870SJakub Sitnicki 	/* We can race with cleanup_net, but if we see a non-NULL
1142576f870SJakub Sitnicki 	 * struct net pointer, pre_exit has not run yet and wait for
1152576f870SJakub Sitnicki 	 * netns_bpf_mutex.
1167f045a49SJakub Sitnicki 	 */
1177f045a49SJakub Sitnicki 	net = net_link->net;
1187f045a49SJakub Sitnicki 	if (!net)
1197f045a49SJakub Sitnicki 		goto out_unlock;
1207f045a49SJakub Sitnicki 
1211559b4aaSJakub Sitnicki 	/* Mark attach point as unused */
1221559b4aaSJakub Sitnicki 	netns_bpf_attach_type_unneed(type);
1231559b4aaSJakub Sitnicki 
124ce3aa9ccSJakub Sitnicki 	/* Remember link position in case of safe delete */
125ce3aa9ccSJakub Sitnicki 	idx = link_index(net, type, net_link);
126ab53cad9SJakub Sitnicki 	list_del(&net_link->node);
1277f045a49SJakub Sitnicki 
128ce3aa9ccSJakub Sitnicki 	cnt = link_count(net, type);
129ce3aa9ccSJakub Sitnicki 	if (!cnt) {
130ce3aa9ccSJakub Sitnicki 		netns_bpf_run_array_detach(net, type);
131ce3aa9ccSJakub Sitnicki 		goto out_unlock;
132ce3aa9ccSJakub Sitnicki 	}
133ce3aa9ccSJakub Sitnicki 
134ce3aa9ccSJakub Sitnicki 	old_array = rcu_dereference_protected(net->bpf.run_array[type],
135ce3aa9ccSJakub Sitnicki 					      lockdep_is_held(&netns_bpf_mutex));
136ce3aa9ccSJakub Sitnicki 	new_array = bpf_prog_array_alloc(cnt, GFP_KERNEL);
137ce3aa9ccSJakub Sitnicki 	if (!new_array) {
138ce3aa9ccSJakub Sitnicki 		WARN_ON(bpf_prog_array_delete_safe_at(old_array, idx));
139ce3aa9ccSJakub Sitnicki 		goto out_unlock;
140ce3aa9ccSJakub Sitnicki 	}
141ce3aa9ccSJakub Sitnicki 	fill_prog_array(net, type, new_array);
142ce3aa9ccSJakub Sitnicki 	rcu_assign_pointer(net->bpf.run_array[type], new_array);
143ce3aa9ccSJakub Sitnicki 	bpf_prog_array_free(old_array);
144ce3aa9ccSJakub Sitnicki 
1457f045a49SJakub Sitnicki out_unlock:
14673b11c2aSAndrii Nakryiko 	net_link->net = NULL;
1477f045a49SJakub Sitnicki 	mutex_unlock(&netns_bpf_mutex);
1487f045a49SJakub Sitnicki }
1497f045a49SJakub Sitnicki 
bpf_netns_link_detach(struct bpf_link * link)15073b11c2aSAndrii Nakryiko static int bpf_netns_link_detach(struct bpf_link *link)
15173b11c2aSAndrii Nakryiko {
15273b11c2aSAndrii Nakryiko 	bpf_netns_link_release(link);
15373b11c2aSAndrii Nakryiko 	return 0;
15473b11c2aSAndrii Nakryiko }
15573b11c2aSAndrii Nakryiko 
bpf_netns_link_dealloc(struct bpf_link * link)1567f045a49SJakub Sitnicki static void bpf_netns_link_dealloc(struct bpf_link *link)
1577f045a49SJakub Sitnicki {
1587f045a49SJakub Sitnicki 	struct bpf_netns_link *net_link =
1597f045a49SJakub Sitnicki 		container_of(link, struct bpf_netns_link, link);
1607f045a49SJakub Sitnicki 
1617f045a49SJakub Sitnicki 	kfree(net_link);
1627f045a49SJakub Sitnicki }
1637f045a49SJakub Sitnicki 
bpf_netns_link_update_prog(struct bpf_link * link,struct bpf_prog * new_prog,struct bpf_prog * old_prog)1647f045a49SJakub Sitnicki static int bpf_netns_link_update_prog(struct bpf_link *link,
1657f045a49SJakub Sitnicki 				      struct bpf_prog *new_prog,
1667f045a49SJakub Sitnicki 				      struct bpf_prog *old_prog)
1677f045a49SJakub Sitnicki {
1687f045a49SJakub Sitnicki 	struct bpf_netns_link *net_link =
1697f045a49SJakub Sitnicki 		container_of(link, struct bpf_netns_link, link);
1707f045a49SJakub Sitnicki 	enum netns_bpf_attach_type type = net_link->netns_type;
171695c1214SJakub Sitnicki 	struct bpf_prog_array *run_array;
1727f045a49SJakub Sitnicki 	struct net *net;
173ce3aa9ccSJakub Sitnicki 	int idx, ret;
1747f045a49SJakub Sitnicki 
1757f045a49SJakub Sitnicki 	if (old_prog && old_prog != link->prog)
1767f045a49SJakub Sitnicki 		return -EPERM;
1777f045a49SJakub Sitnicki 	if (new_prog->type != link->prog->type)
1787f045a49SJakub Sitnicki 		return -EINVAL;
1797f045a49SJakub Sitnicki 
1807f045a49SJakub Sitnicki 	mutex_lock(&netns_bpf_mutex);
1817f045a49SJakub Sitnicki 
1827f045a49SJakub Sitnicki 	net = net_link->net;
1837f045a49SJakub Sitnicki 	if (!net || !check_net(net)) {
1847f045a49SJakub Sitnicki 		/* Link auto-detached or netns dying */
1857f045a49SJakub Sitnicki 		ret = -ENOLINK;
1867f045a49SJakub Sitnicki 		goto out_unlock;
1877f045a49SJakub Sitnicki 	}
1887f045a49SJakub Sitnicki 
189695c1214SJakub Sitnicki 	run_array = rcu_dereference_protected(net->bpf.run_array[type],
190695c1214SJakub Sitnicki 					      lockdep_is_held(&netns_bpf_mutex));
191ce3aa9ccSJakub Sitnicki 	idx = link_index(net, type, net_link);
192ce3aa9ccSJakub Sitnicki 	ret = bpf_prog_array_update_at(run_array, idx, new_prog);
193ce3aa9ccSJakub Sitnicki 	if (ret)
194ce3aa9ccSJakub Sitnicki 		goto out_unlock;
195695c1214SJakub Sitnicki 
1967f045a49SJakub Sitnicki 	old_prog = xchg(&link->prog, new_prog);
1977f045a49SJakub Sitnicki 	bpf_prog_put(old_prog);
1987f045a49SJakub Sitnicki 
1997f045a49SJakub Sitnicki out_unlock:
2007f045a49SJakub Sitnicki 	mutex_unlock(&netns_bpf_mutex);
2017f045a49SJakub Sitnicki 	return ret;
2027f045a49SJakub Sitnicki }
2037f045a49SJakub Sitnicki 
bpf_netns_link_fill_info(const struct bpf_link * link,struct bpf_link_info * info)2047f045a49SJakub Sitnicki static int bpf_netns_link_fill_info(const struct bpf_link *link,
2057f045a49SJakub Sitnicki 				    struct bpf_link_info *info)
2067f045a49SJakub Sitnicki {
2077f045a49SJakub Sitnicki 	const struct bpf_netns_link *net_link =
2087f045a49SJakub Sitnicki 		container_of(link, struct bpf_netns_link, link);
2097f045a49SJakub Sitnicki 	unsigned int inum = 0;
2107f045a49SJakub Sitnicki 	struct net *net;
2117f045a49SJakub Sitnicki 
2127f045a49SJakub Sitnicki 	mutex_lock(&netns_bpf_mutex);
2137f045a49SJakub Sitnicki 	net = net_link->net;
2147f045a49SJakub Sitnicki 	if (net && check_net(net))
2157f045a49SJakub Sitnicki 		inum = net->ns.inum;
2167f045a49SJakub Sitnicki 	mutex_unlock(&netns_bpf_mutex);
2177f045a49SJakub Sitnicki 
2187f045a49SJakub Sitnicki 	info->netns.netns_ino = inum;
2197f045a49SJakub Sitnicki 	info->netns.attach_type = net_link->type;
2207f045a49SJakub Sitnicki 	return 0;
2217f045a49SJakub Sitnicki }
2227f045a49SJakub Sitnicki 
bpf_netns_link_show_fdinfo(const struct bpf_link * link,struct seq_file * seq)2237f045a49SJakub Sitnicki static void bpf_netns_link_show_fdinfo(const struct bpf_link *link,
2247f045a49SJakub Sitnicki 				       struct seq_file *seq)
2257f045a49SJakub Sitnicki {
2267f045a49SJakub Sitnicki 	struct bpf_link_info info = {};
2277f045a49SJakub Sitnicki 
2287f045a49SJakub Sitnicki 	bpf_netns_link_fill_info(link, &info);
2297f045a49SJakub Sitnicki 	seq_printf(seq,
2307f045a49SJakub Sitnicki 		   "netns_ino:\t%u\n"
2317f045a49SJakub Sitnicki 		   "attach_type:\t%u\n",
2327f045a49SJakub Sitnicki 		   info.netns.netns_ino,
2337f045a49SJakub Sitnicki 		   info.netns.attach_type);
2347f045a49SJakub Sitnicki }
2357f045a49SJakub Sitnicki 
2367f045a49SJakub Sitnicki static const struct bpf_link_ops bpf_netns_link_ops = {
2377f045a49SJakub Sitnicki 	.release = bpf_netns_link_release,
2387f045a49SJakub Sitnicki 	.dealloc = bpf_netns_link_dealloc,
23973b11c2aSAndrii Nakryiko 	.detach = bpf_netns_link_detach,
2407f045a49SJakub Sitnicki 	.update_prog = bpf_netns_link_update_prog,
2417f045a49SJakub Sitnicki 	.fill_link_info = bpf_netns_link_fill_info,
2427f045a49SJakub Sitnicki 	.show_fdinfo = bpf_netns_link_show_fdinfo,
2437f045a49SJakub Sitnicki };
2447f045a49SJakub Sitnicki 
245695c1214SJakub Sitnicki /* Must be called with netns_bpf_mutex held. */
__netns_bpf_prog_query(const union bpf_attr * attr,union bpf_attr __user * uattr,struct net * net,enum netns_bpf_attach_type type)246695c1214SJakub Sitnicki static int __netns_bpf_prog_query(const union bpf_attr *attr,
247695c1214SJakub Sitnicki 				  union bpf_attr __user *uattr,
248695c1214SJakub Sitnicki 				  struct net *net,
249695c1214SJakub Sitnicki 				  enum netns_bpf_attach_type type)
250695c1214SJakub Sitnicki {
251695c1214SJakub Sitnicki 	__u32 __user *prog_ids = u64_to_user_ptr(attr->query.prog_ids);
252695c1214SJakub Sitnicki 	struct bpf_prog_array *run_array;
253695c1214SJakub Sitnicki 	u32 prog_cnt = 0, flags = 0;
254695c1214SJakub Sitnicki 
255695c1214SJakub Sitnicki 	run_array = rcu_dereference_protected(net->bpf.run_array[type],
256695c1214SJakub Sitnicki 					      lockdep_is_held(&netns_bpf_mutex));
257695c1214SJakub Sitnicki 	if (run_array)
258695c1214SJakub Sitnicki 		prog_cnt = bpf_prog_array_length(run_array);
259695c1214SJakub Sitnicki 
260695c1214SJakub Sitnicki 	if (copy_to_user(&uattr->query.attach_flags, &flags, sizeof(flags)))
261695c1214SJakub Sitnicki 		return -EFAULT;
262695c1214SJakub Sitnicki 	if (copy_to_user(&uattr->query.prog_cnt, &prog_cnt, sizeof(prog_cnt)))
263695c1214SJakub Sitnicki 		return -EFAULT;
264695c1214SJakub Sitnicki 	if (!attr->query.prog_cnt || !prog_ids || !prog_cnt)
265695c1214SJakub Sitnicki 		return 0;
266695c1214SJakub Sitnicki 
267695c1214SJakub Sitnicki 	return bpf_prog_array_copy_to_user(run_array, prog_ids,
268695c1214SJakub Sitnicki 					   attr->query.prog_cnt);
269695c1214SJakub Sitnicki }
270695c1214SJakub Sitnicki 
netns_bpf_prog_query(const union bpf_attr * attr,union bpf_attr __user * uattr)271b27f7bb5SJakub Sitnicki int netns_bpf_prog_query(const union bpf_attr *attr,
272b27f7bb5SJakub Sitnicki 			 union bpf_attr __user *uattr)
273b27f7bb5SJakub Sitnicki {
274b27f7bb5SJakub Sitnicki 	enum netns_bpf_attach_type type;
275b27f7bb5SJakub Sitnicki 	struct net *net;
276695c1214SJakub Sitnicki 	int ret;
277b27f7bb5SJakub Sitnicki 
278b27f7bb5SJakub Sitnicki 	if (attr->query.query_flags)
279b27f7bb5SJakub Sitnicki 		return -EINVAL;
280b27f7bb5SJakub Sitnicki 
281b27f7bb5SJakub Sitnicki 	type = to_netns_bpf_attach_type(attr->query.attach_type);
282b27f7bb5SJakub Sitnicki 	if (type < 0)
283b27f7bb5SJakub Sitnicki 		return -EINVAL;
284b27f7bb5SJakub Sitnicki 
285b27f7bb5SJakub Sitnicki 	net = get_net_ns_by_fd(attr->query.target_fd);
286b27f7bb5SJakub Sitnicki 	if (IS_ERR(net))
287b27f7bb5SJakub Sitnicki 		return PTR_ERR(net);
288b27f7bb5SJakub Sitnicki 
289695c1214SJakub Sitnicki 	mutex_lock(&netns_bpf_mutex);
290695c1214SJakub Sitnicki 	ret = __netns_bpf_prog_query(attr, uattr, net, type);
291695c1214SJakub Sitnicki 	mutex_unlock(&netns_bpf_mutex);
292b27f7bb5SJakub Sitnicki 
293b27f7bb5SJakub Sitnicki 	put_net(net);
294695c1214SJakub Sitnicki 	return ret;
295b27f7bb5SJakub Sitnicki }
296b27f7bb5SJakub Sitnicki 
netns_bpf_prog_attach(const union bpf_attr * attr,struct bpf_prog * prog)297b27f7bb5SJakub Sitnicki int netns_bpf_prog_attach(const union bpf_attr *attr, struct bpf_prog *prog)
298b27f7bb5SJakub Sitnicki {
299695c1214SJakub Sitnicki 	struct bpf_prog_array *run_array;
300b27f7bb5SJakub Sitnicki 	enum netns_bpf_attach_type type;
3013b701699SJakub Sitnicki 	struct bpf_prog *attached;
302b27f7bb5SJakub Sitnicki 	struct net *net;
303b27f7bb5SJakub Sitnicki 	int ret;
304b27f7bb5SJakub Sitnicki 
3051b514239SLorenz Bauer 	if (attr->target_fd || attr->attach_flags || attr->replace_bpf_fd)
3061b514239SLorenz Bauer 		return -EINVAL;
3071b514239SLorenz Bauer 
308b27f7bb5SJakub Sitnicki 	type = to_netns_bpf_attach_type(attr->attach_type);
309b27f7bb5SJakub Sitnicki 	if (type < 0)
310b27f7bb5SJakub Sitnicki 		return -EINVAL;
311b27f7bb5SJakub Sitnicki 
312b27f7bb5SJakub Sitnicki 	net = current->nsproxy->net_ns;
313b27f7bb5SJakub Sitnicki 	mutex_lock(&netns_bpf_mutex);
3147f045a49SJakub Sitnicki 
3157f045a49SJakub Sitnicki 	/* Attaching prog directly is not compatible with links */
316ab53cad9SJakub Sitnicki 	if (!list_empty(&net->bpf.links[type])) {
3177f045a49SJakub Sitnicki 		ret = -EEXIST;
3187f045a49SJakub Sitnicki 		goto out_unlock;
3197f045a49SJakub Sitnicki 	}
3207f045a49SJakub Sitnicki 
321b27f7bb5SJakub Sitnicki 	switch (type) {
322b27f7bb5SJakub Sitnicki 	case NETNS_BPF_FLOW_DISSECTOR:
3233b701699SJakub Sitnicki 		ret = flow_dissector_bpf_prog_attach_check(net, prog);
324b27f7bb5SJakub Sitnicki 		break;
325b27f7bb5SJakub Sitnicki 	default:
326b27f7bb5SJakub Sitnicki 		ret = -EINVAL;
327b27f7bb5SJakub Sitnicki 		break;
328b27f7bb5SJakub Sitnicki 	}
3293b701699SJakub Sitnicki 	if (ret)
3303b701699SJakub Sitnicki 		goto out_unlock;
3313b701699SJakub Sitnicki 
332695c1214SJakub Sitnicki 	attached = net->bpf.progs[type];
3333b701699SJakub Sitnicki 	if (attached == prog) {
3343b701699SJakub Sitnicki 		/* The same program cannot be attached twice */
3353b701699SJakub Sitnicki 		ret = -EINVAL;
3363b701699SJakub Sitnicki 		goto out_unlock;
3373b701699SJakub Sitnicki 	}
338695c1214SJakub Sitnicki 
339695c1214SJakub Sitnicki 	run_array = rcu_dereference_protected(net->bpf.run_array[type],
340695c1214SJakub Sitnicki 					      lockdep_is_held(&netns_bpf_mutex));
341695c1214SJakub Sitnicki 	if (run_array) {
342695c1214SJakub Sitnicki 		WRITE_ONCE(run_array->items[0].prog, prog);
343695c1214SJakub Sitnicki 	} else {
344695c1214SJakub Sitnicki 		run_array = bpf_prog_array_alloc(1, GFP_KERNEL);
345695c1214SJakub Sitnicki 		if (!run_array) {
346695c1214SJakub Sitnicki 			ret = -ENOMEM;
347695c1214SJakub Sitnicki 			goto out_unlock;
348695c1214SJakub Sitnicki 		}
349695c1214SJakub Sitnicki 		run_array->items[0].prog = prog;
350695c1214SJakub Sitnicki 		rcu_assign_pointer(net->bpf.run_array[type], run_array);
351695c1214SJakub Sitnicki 	}
352695c1214SJakub Sitnicki 
353695c1214SJakub Sitnicki 	net->bpf.progs[type] = prog;
3543b701699SJakub Sitnicki 	if (attached)
3553b701699SJakub Sitnicki 		bpf_prog_put(attached);
3563b701699SJakub Sitnicki 
3577f045a49SJakub Sitnicki out_unlock:
358b27f7bb5SJakub Sitnicki 	mutex_unlock(&netns_bpf_mutex);
359b27f7bb5SJakub Sitnicki 
360b27f7bb5SJakub Sitnicki 	return ret;
361b27f7bb5SJakub Sitnicki }
362b27f7bb5SJakub Sitnicki 
363b27f7bb5SJakub Sitnicki /* Must be called with netns_bpf_mutex held. */
__netns_bpf_prog_detach(struct net * net,enum netns_bpf_attach_type type,struct bpf_prog * old)364b27f7bb5SJakub Sitnicki static int __netns_bpf_prog_detach(struct net *net,
3654ac2add6SLorenz Bauer 				   enum netns_bpf_attach_type type,
3664ac2add6SLorenz Bauer 				   struct bpf_prog *old)
367b27f7bb5SJakub Sitnicki {
368b27f7bb5SJakub Sitnicki 	struct bpf_prog *attached;
369b27f7bb5SJakub Sitnicki 
3707f045a49SJakub Sitnicki 	/* Progs attached via links cannot be detached */
371ab53cad9SJakub Sitnicki 	if (!list_empty(&net->bpf.links[type]))
3727f045a49SJakub Sitnicki 		return -EINVAL;
3737f045a49SJakub Sitnicki 
374695c1214SJakub Sitnicki 	attached = net->bpf.progs[type];
3754ac2add6SLorenz Bauer 	if (!attached || attached != old)
376b27f7bb5SJakub Sitnicki 		return -ENOENT;
377695c1214SJakub Sitnicki 	netns_bpf_run_array_detach(net, type);
378695c1214SJakub Sitnicki 	net->bpf.progs[type] = NULL;
379b27f7bb5SJakub Sitnicki 	bpf_prog_put(attached);
380b27f7bb5SJakub Sitnicki 	return 0;
381b27f7bb5SJakub Sitnicki }
382b27f7bb5SJakub Sitnicki 
netns_bpf_prog_detach(const union bpf_attr * attr,enum bpf_prog_type ptype)3834ac2add6SLorenz Bauer int netns_bpf_prog_detach(const union bpf_attr *attr, enum bpf_prog_type ptype)
384b27f7bb5SJakub Sitnicki {
385b27f7bb5SJakub Sitnicki 	enum netns_bpf_attach_type type;
3864ac2add6SLorenz Bauer 	struct bpf_prog *prog;
387b27f7bb5SJakub Sitnicki 	int ret;
388b27f7bb5SJakub Sitnicki 
3894ac2add6SLorenz Bauer 	if (attr->target_fd)
3904ac2add6SLorenz Bauer 		return -EINVAL;
3914ac2add6SLorenz Bauer 
392b27f7bb5SJakub Sitnicki 	type = to_netns_bpf_attach_type(attr->attach_type);
393b27f7bb5SJakub Sitnicki 	if (type < 0)
394b27f7bb5SJakub Sitnicki 		return -EINVAL;
395b27f7bb5SJakub Sitnicki 
3964ac2add6SLorenz Bauer 	prog = bpf_prog_get_type(attr->attach_bpf_fd, ptype);
3974ac2add6SLorenz Bauer 	if (IS_ERR(prog))
3984ac2add6SLorenz Bauer 		return PTR_ERR(prog);
3994ac2add6SLorenz Bauer 
400b27f7bb5SJakub Sitnicki 	mutex_lock(&netns_bpf_mutex);
4014ac2add6SLorenz Bauer 	ret = __netns_bpf_prog_detach(current->nsproxy->net_ns, type, prog);
402b27f7bb5SJakub Sitnicki 	mutex_unlock(&netns_bpf_mutex);
403b27f7bb5SJakub Sitnicki 
4044ac2add6SLorenz Bauer 	bpf_prog_put(prog);
4054ac2add6SLorenz Bauer 
406b27f7bb5SJakub Sitnicki 	return ret;
407b27f7bb5SJakub Sitnicki }
408b27f7bb5SJakub Sitnicki 
netns_bpf_max_progs(enum netns_bpf_attach_type type)409ce3aa9ccSJakub Sitnicki static int netns_bpf_max_progs(enum netns_bpf_attach_type type)
410ce3aa9ccSJakub Sitnicki {
411ce3aa9ccSJakub Sitnicki 	switch (type) {
412ce3aa9ccSJakub Sitnicki 	case NETNS_BPF_FLOW_DISSECTOR:
413ce3aa9ccSJakub Sitnicki 		return 1;
414e9ddbb77SJakub Sitnicki 	case NETNS_BPF_SK_LOOKUP:
415e9ddbb77SJakub Sitnicki 		return 64;
416ce3aa9ccSJakub Sitnicki 	default:
417ce3aa9ccSJakub Sitnicki 		return 0;
418ce3aa9ccSJakub Sitnicki 	}
419ce3aa9ccSJakub Sitnicki }
420ce3aa9ccSJakub Sitnicki 
netns_bpf_link_attach(struct net * net,struct bpf_link * link,enum netns_bpf_attach_type type)4217f045a49SJakub Sitnicki static int netns_bpf_link_attach(struct net *net, struct bpf_link *link,
4227f045a49SJakub Sitnicki 				 enum netns_bpf_attach_type type)
4237f045a49SJakub Sitnicki {
424ab53cad9SJakub Sitnicki 	struct bpf_netns_link *net_link =
425ab53cad9SJakub Sitnicki 		container_of(link, struct bpf_netns_link, link);
426695c1214SJakub Sitnicki 	struct bpf_prog_array *run_array;
427ce3aa9ccSJakub Sitnicki 	int cnt, err;
4287f045a49SJakub Sitnicki 
4297f045a49SJakub Sitnicki 	mutex_lock(&netns_bpf_mutex);
4307f045a49SJakub Sitnicki 
431ce3aa9ccSJakub Sitnicki 	cnt = link_count(net, type);
432ce3aa9ccSJakub Sitnicki 	if (cnt >= netns_bpf_max_progs(type)) {
4337f045a49SJakub Sitnicki 		err = -E2BIG;
4347f045a49SJakub Sitnicki 		goto out_unlock;
4357f045a49SJakub Sitnicki 	}
4367f045a49SJakub Sitnicki 	/* Links are not compatible with attaching prog directly */
437695c1214SJakub Sitnicki 	if (net->bpf.progs[type]) {
4387f045a49SJakub Sitnicki 		err = -EEXIST;
4397f045a49SJakub Sitnicki 		goto out_unlock;
4407f045a49SJakub Sitnicki 	}
4417f045a49SJakub Sitnicki 
4427f045a49SJakub Sitnicki 	switch (type) {
4437f045a49SJakub Sitnicki 	case NETNS_BPF_FLOW_DISSECTOR:
4443b701699SJakub Sitnicki 		err = flow_dissector_bpf_prog_attach_check(net, link->prog);
4457f045a49SJakub Sitnicki 		break;
446e9ddbb77SJakub Sitnicki 	case NETNS_BPF_SK_LOOKUP:
447e9ddbb77SJakub Sitnicki 		err = 0; /* nothing to check */
448e9ddbb77SJakub Sitnicki 		break;
4497f045a49SJakub Sitnicki 	default:
4507f045a49SJakub Sitnicki 		err = -EINVAL;
4517f045a49SJakub Sitnicki 		break;
4527f045a49SJakub Sitnicki 	}
4537f045a49SJakub Sitnicki 	if (err)
4547f045a49SJakub Sitnicki 		goto out_unlock;
4557f045a49SJakub Sitnicki 
456ce3aa9ccSJakub Sitnicki 	run_array = bpf_prog_array_alloc(cnt + 1, GFP_KERNEL);
457695c1214SJakub Sitnicki 	if (!run_array) {
458695c1214SJakub Sitnicki 		err = -ENOMEM;
459695c1214SJakub Sitnicki 		goto out_unlock;
460695c1214SJakub Sitnicki 	}
461695c1214SJakub Sitnicki 
462ab53cad9SJakub Sitnicki 	list_add_tail(&net_link->node, &net->bpf.links[type]);
4637f045a49SJakub Sitnicki 
464ce3aa9ccSJakub Sitnicki 	fill_prog_array(net, type, run_array);
465ce3aa9ccSJakub Sitnicki 	run_array = rcu_replace_pointer(net->bpf.run_array[type], run_array,
466ce3aa9ccSJakub Sitnicki 					lockdep_is_held(&netns_bpf_mutex));
467ce3aa9ccSJakub Sitnicki 	bpf_prog_array_free(run_array);
468ce3aa9ccSJakub Sitnicki 
4691559b4aaSJakub Sitnicki 	/* Mark attach point as used */
4701559b4aaSJakub Sitnicki 	netns_bpf_attach_type_need(type);
4711559b4aaSJakub Sitnicki 
4727f045a49SJakub Sitnicki out_unlock:
4737f045a49SJakub Sitnicki 	mutex_unlock(&netns_bpf_mutex);
4747f045a49SJakub Sitnicki 	return err;
4757f045a49SJakub Sitnicki }
4767f045a49SJakub Sitnicki 
netns_bpf_link_create(const union bpf_attr * attr,struct bpf_prog * prog)4777f045a49SJakub Sitnicki int netns_bpf_link_create(const union bpf_attr *attr, struct bpf_prog *prog)
4787f045a49SJakub Sitnicki {
4797f045a49SJakub Sitnicki 	enum netns_bpf_attach_type netns_type;
4807f045a49SJakub Sitnicki 	struct bpf_link_primer link_primer;
4817f045a49SJakub Sitnicki 	struct bpf_netns_link *net_link;
4827f045a49SJakub Sitnicki 	enum bpf_attach_type type;
4837f045a49SJakub Sitnicki 	struct net *net;
4847f045a49SJakub Sitnicki 	int err;
4857f045a49SJakub Sitnicki 
4867f045a49SJakub Sitnicki 	if (attr->link_create.flags)
4877f045a49SJakub Sitnicki 		return -EINVAL;
4887f045a49SJakub Sitnicki 
4897f045a49SJakub Sitnicki 	type = attr->link_create.attach_type;
4907f045a49SJakub Sitnicki 	netns_type = to_netns_bpf_attach_type(type);
4917f045a49SJakub Sitnicki 	if (netns_type < 0)
4927f045a49SJakub Sitnicki 		return -EINVAL;
4937f045a49SJakub Sitnicki 
4947f045a49SJakub Sitnicki 	net = get_net_ns_by_fd(attr->link_create.target_fd);
4957f045a49SJakub Sitnicki 	if (IS_ERR(net))
4967f045a49SJakub Sitnicki 		return PTR_ERR(net);
4977f045a49SJakub Sitnicki 
4987f045a49SJakub Sitnicki 	net_link = kzalloc(sizeof(*net_link), GFP_USER);
4997f045a49SJakub Sitnicki 	if (!net_link) {
5007f045a49SJakub Sitnicki 		err = -ENOMEM;
5017f045a49SJakub Sitnicki 		goto out_put_net;
5027f045a49SJakub Sitnicki 	}
5037f045a49SJakub Sitnicki 	bpf_link_init(&net_link->link, BPF_LINK_TYPE_NETNS,
5047f045a49SJakub Sitnicki 		      &bpf_netns_link_ops, prog);
5057f045a49SJakub Sitnicki 	net_link->net = net;
5067f045a49SJakub Sitnicki 	net_link->type = type;
5077f045a49SJakub Sitnicki 	net_link->netns_type = netns_type;
5087f045a49SJakub Sitnicki 
5097f045a49SJakub Sitnicki 	err = bpf_link_prime(&net_link->link, &link_primer);
5107f045a49SJakub Sitnicki 	if (err) {
5117f045a49SJakub Sitnicki 		kfree(net_link);
5127f045a49SJakub Sitnicki 		goto out_put_net;
5137f045a49SJakub Sitnicki 	}
5147f045a49SJakub Sitnicki 
5157f045a49SJakub Sitnicki 	err = netns_bpf_link_attach(net, &net_link->link, netns_type);
5167f045a49SJakub Sitnicki 	if (err) {
5177f045a49SJakub Sitnicki 		bpf_link_cleanup(&link_primer);
5187f045a49SJakub Sitnicki 		goto out_put_net;
5197f045a49SJakub Sitnicki 	}
5207f045a49SJakub Sitnicki 
5217f045a49SJakub Sitnicki 	put_net(net);
5227f045a49SJakub Sitnicki 	return bpf_link_settle(&link_primer);
5237f045a49SJakub Sitnicki 
5247f045a49SJakub Sitnicki out_put_net:
5257f045a49SJakub Sitnicki 	put_net(net);
5267f045a49SJakub Sitnicki 	return err;
5277f045a49SJakub Sitnicki }
5287f045a49SJakub Sitnicki 
netns_bpf_pernet_init(struct net * net)529ab53cad9SJakub Sitnicki static int __net_init netns_bpf_pernet_init(struct net *net)
530ab53cad9SJakub Sitnicki {
531ab53cad9SJakub Sitnicki 	int type;
532ab53cad9SJakub Sitnicki 
533ab53cad9SJakub Sitnicki 	for (type = 0; type < MAX_NETNS_BPF_ATTACH_TYPE; type++)
534ab53cad9SJakub Sitnicki 		INIT_LIST_HEAD(&net->bpf.links[type]);
535ab53cad9SJakub Sitnicki 
536ab53cad9SJakub Sitnicki 	return 0;
537ab53cad9SJakub Sitnicki }
538ab53cad9SJakub Sitnicki 
netns_bpf_pernet_pre_exit(struct net * net)539b27f7bb5SJakub Sitnicki static void __net_exit netns_bpf_pernet_pre_exit(struct net *net)
540b27f7bb5SJakub Sitnicki {
541b27f7bb5SJakub Sitnicki 	enum netns_bpf_attach_type type;
542ab53cad9SJakub Sitnicki 	struct bpf_netns_link *net_link;
543b27f7bb5SJakub Sitnicki 
544b27f7bb5SJakub Sitnicki 	mutex_lock(&netns_bpf_mutex);
5457f045a49SJakub Sitnicki 	for (type = 0; type < MAX_NETNS_BPF_ATTACH_TYPE; type++) {
546695c1214SJakub Sitnicki 		netns_bpf_run_array_detach(net, type);
5471559b4aaSJakub Sitnicki 		list_for_each_entry(net_link, &net->bpf.links[type], node) {
548ab53cad9SJakub Sitnicki 			net_link->net = NULL; /* auto-detach link */
5491559b4aaSJakub Sitnicki 			netns_bpf_attach_type_unneed(type);
5501559b4aaSJakub Sitnicki 		}
551ab53cad9SJakub Sitnicki 		if (net->bpf.progs[type])
552695c1214SJakub Sitnicki 			bpf_prog_put(net->bpf.progs[type]);
5537f045a49SJakub Sitnicki 	}
554b27f7bb5SJakub Sitnicki 	mutex_unlock(&netns_bpf_mutex);
555b27f7bb5SJakub Sitnicki }
556b27f7bb5SJakub Sitnicki 
557b27f7bb5SJakub Sitnicki static struct pernet_operations netns_bpf_pernet_ops __net_initdata = {
558ab53cad9SJakub Sitnicki 	.init = netns_bpf_pernet_init,
559b27f7bb5SJakub Sitnicki 	.pre_exit = netns_bpf_pernet_pre_exit,
560b27f7bb5SJakub Sitnicki };
561b27f7bb5SJakub Sitnicki 
netns_bpf_init(void)562b27f7bb5SJakub Sitnicki static int __init netns_bpf_init(void)
563b27f7bb5SJakub Sitnicki {
564b27f7bb5SJakub Sitnicki 	return register_pernet_subsys(&netns_bpf_pernet_ops);
565b27f7bb5SJakub Sitnicki }
566b27f7bb5SJakub Sitnicki 
567b27f7bb5SJakub Sitnicki subsys_initcall(netns_bpf_init);
568