xref: /openbmc/linux/kernel/bpf/net_namespace.c (revision 343ead28)
1b27f7bb5SJakub Sitnicki // SPDX-License-Identifier: GPL-2.0
2b27f7bb5SJakub Sitnicki 
3b27f7bb5SJakub Sitnicki #include <linux/bpf.h>
4b27f7bb5SJakub Sitnicki #include <linux/filter.h>
5b27f7bb5SJakub Sitnicki #include <net/net_namespace.h>
6b27f7bb5SJakub Sitnicki 
7b27f7bb5SJakub Sitnicki /*
8b27f7bb5SJakub Sitnicki  * Functions to manage BPF programs attached to netns
9b27f7bb5SJakub Sitnicki  */
10b27f7bb5SJakub Sitnicki 
117f045a49SJakub Sitnicki struct bpf_netns_link {
127f045a49SJakub Sitnicki 	struct bpf_link	link;
137f045a49SJakub Sitnicki 	enum bpf_attach_type type;
147f045a49SJakub Sitnicki 	enum netns_bpf_attach_type netns_type;
157f045a49SJakub Sitnicki 
167f045a49SJakub Sitnicki 	/* We don't hold a ref to net in order to auto-detach the link
177f045a49SJakub Sitnicki 	 * when netns is going away. Instead we rely on pernet
187f045a49SJakub Sitnicki 	 * pre_exit callback to clear this pointer. Must be accessed
197f045a49SJakub Sitnicki 	 * with netns_bpf_mutex held.
207f045a49SJakub Sitnicki 	 */
217f045a49SJakub Sitnicki 	struct net *net;
22ab53cad9SJakub Sitnicki 	struct list_head node; /* node in list of links attached to net */
237f045a49SJakub Sitnicki };
247f045a49SJakub Sitnicki 
25b27f7bb5SJakub Sitnicki /* Protects updates to netns_bpf */
26b27f7bb5SJakub Sitnicki DEFINE_MUTEX(netns_bpf_mutex);
27b27f7bb5SJakub Sitnicki 
281559b4aaSJakub Sitnicki static void netns_bpf_attach_type_unneed(enum netns_bpf_attach_type type)
291559b4aaSJakub Sitnicki {
301559b4aaSJakub Sitnicki 	switch (type) {
31343ead28SJakub Sitnicki #ifdef CONFIG_INET
321559b4aaSJakub Sitnicki 	case NETNS_BPF_SK_LOOKUP:
331559b4aaSJakub Sitnicki 		static_branch_dec(&bpf_sk_lookup_enabled);
341559b4aaSJakub Sitnicki 		break;
35343ead28SJakub Sitnicki #endif
361559b4aaSJakub Sitnicki 	default:
371559b4aaSJakub Sitnicki 		break;
381559b4aaSJakub Sitnicki 	}
391559b4aaSJakub Sitnicki }
401559b4aaSJakub Sitnicki 
411559b4aaSJakub Sitnicki static void netns_bpf_attach_type_need(enum netns_bpf_attach_type type)
421559b4aaSJakub Sitnicki {
431559b4aaSJakub Sitnicki 	switch (type) {
44343ead28SJakub Sitnicki #ifdef CONFIG_INET
451559b4aaSJakub Sitnicki 	case NETNS_BPF_SK_LOOKUP:
461559b4aaSJakub Sitnicki 		static_branch_inc(&bpf_sk_lookup_enabled);
471559b4aaSJakub Sitnicki 		break;
48343ead28SJakub Sitnicki #endif
491559b4aaSJakub Sitnicki 	default:
501559b4aaSJakub Sitnicki 		break;
511559b4aaSJakub Sitnicki 	}
521559b4aaSJakub Sitnicki }
531559b4aaSJakub Sitnicki 
547f045a49SJakub Sitnicki /* Must be called with netns_bpf_mutex held. */
55695c1214SJakub Sitnicki static void netns_bpf_run_array_detach(struct net *net,
56695c1214SJakub Sitnicki 				       enum netns_bpf_attach_type type)
57695c1214SJakub Sitnicki {
58695c1214SJakub Sitnicki 	struct bpf_prog_array *run_array;
59695c1214SJakub Sitnicki 
60695c1214SJakub Sitnicki 	run_array = rcu_replace_pointer(net->bpf.run_array[type], NULL,
61695c1214SJakub Sitnicki 					lockdep_is_held(&netns_bpf_mutex));
62695c1214SJakub Sitnicki 	bpf_prog_array_free(run_array);
63695c1214SJakub Sitnicki }
64695c1214SJakub Sitnicki 
65ce3aa9ccSJakub Sitnicki static int link_index(struct net *net, enum netns_bpf_attach_type type,
66ce3aa9ccSJakub Sitnicki 		      struct bpf_netns_link *link)
67ce3aa9ccSJakub Sitnicki {
68ce3aa9ccSJakub Sitnicki 	struct bpf_netns_link *pos;
69ce3aa9ccSJakub Sitnicki 	int i = 0;
70ce3aa9ccSJakub Sitnicki 
71ce3aa9ccSJakub Sitnicki 	list_for_each_entry(pos, &net->bpf.links[type], node) {
72ce3aa9ccSJakub Sitnicki 		if (pos == link)
73ce3aa9ccSJakub Sitnicki 			return i;
74ce3aa9ccSJakub Sitnicki 		i++;
75ce3aa9ccSJakub Sitnicki 	}
76ce3aa9ccSJakub Sitnicki 	return -ENOENT;
77ce3aa9ccSJakub Sitnicki }
78ce3aa9ccSJakub Sitnicki 
79ce3aa9ccSJakub Sitnicki static int link_count(struct net *net, enum netns_bpf_attach_type type)
80ce3aa9ccSJakub Sitnicki {
81ce3aa9ccSJakub Sitnicki 	struct list_head *pos;
82ce3aa9ccSJakub Sitnicki 	int i = 0;
83ce3aa9ccSJakub Sitnicki 
84ce3aa9ccSJakub Sitnicki 	list_for_each(pos, &net->bpf.links[type])
85ce3aa9ccSJakub Sitnicki 		i++;
86ce3aa9ccSJakub Sitnicki 	return i;
87ce3aa9ccSJakub Sitnicki }
88ce3aa9ccSJakub Sitnicki 
89ce3aa9ccSJakub Sitnicki static void fill_prog_array(struct net *net, enum netns_bpf_attach_type type,
90ce3aa9ccSJakub Sitnicki 			    struct bpf_prog_array *prog_array)
91ce3aa9ccSJakub Sitnicki {
92ce3aa9ccSJakub Sitnicki 	struct bpf_netns_link *pos;
93ce3aa9ccSJakub Sitnicki 	unsigned int i = 0;
94ce3aa9ccSJakub Sitnicki 
95ce3aa9ccSJakub Sitnicki 	list_for_each_entry(pos, &net->bpf.links[type], node) {
96ce3aa9ccSJakub Sitnicki 		prog_array->items[i].prog = pos->link.prog;
97ce3aa9ccSJakub Sitnicki 		i++;
98ce3aa9ccSJakub Sitnicki 	}
99ce3aa9ccSJakub Sitnicki }
100ce3aa9ccSJakub Sitnicki 
1017f045a49SJakub Sitnicki static void bpf_netns_link_release(struct bpf_link *link)
1027f045a49SJakub Sitnicki {
1037f045a49SJakub Sitnicki 	struct bpf_netns_link *net_link =
1047f045a49SJakub Sitnicki 		container_of(link, struct bpf_netns_link, link);
1057f045a49SJakub Sitnicki 	enum netns_bpf_attach_type type = net_link->netns_type;
106ce3aa9ccSJakub Sitnicki 	struct bpf_prog_array *old_array, *new_array;
1077f045a49SJakub Sitnicki 	struct net *net;
108ce3aa9ccSJakub Sitnicki 	int cnt, idx;
1097f045a49SJakub Sitnicki 
1107f045a49SJakub Sitnicki 	mutex_lock(&netns_bpf_mutex);
1117f045a49SJakub Sitnicki 
1122576f870SJakub Sitnicki 	/* We can race with cleanup_net, but if we see a non-NULL
1132576f870SJakub Sitnicki 	 * struct net pointer, pre_exit has not run yet and wait for
1142576f870SJakub Sitnicki 	 * netns_bpf_mutex.
1157f045a49SJakub Sitnicki 	 */
1167f045a49SJakub Sitnicki 	net = net_link->net;
1177f045a49SJakub Sitnicki 	if (!net)
1187f045a49SJakub Sitnicki 		goto out_unlock;
1197f045a49SJakub Sitnicki 
1201559b4aaSJakub Sitnicki 	/* Mark attach point as unused */
1211559b4aaSJakub Sitnicki 	netns_bpf_attach_type_unneed(type);
1221559b4aaSJakub Sitnicki 
123ce3aa9ccSJakub Sitnicki 	/* Remember link position in case of safe delete */
124ce3aa9ccSJakub Sitnicki 	idx = link_index(net, type, net_link);
125ab53cad9SJakub Sitnicki 	list_del(&net_link->node);
1267f045a49SJakub Sitnicki 
127ce3aa9ccSJakub Sitnicki 	cnt = link_count(net, type);
128ce3aa9ccSJakub Sitnicki 	if (!cnt) {
129ce3aa9ccSJakub Sitnicki 		netns_bpf_run_array_detach(net, type);
130ce3aa9ccSJakub Sitnicki 		goto out_unlock;
131ce3aa9ccSJakub Sitnicki 	}
132ce3aa9ccSJakub Sitnicki 
133ce3aa9ccSJakub Sitnicki 	old_array = rcu_dereference_protected(net->bpf.run_array[type],
134ce3aa9ccSJakub Sitnicki 					      lockdep_is_held(&netns_bpf_mutex));
135ce3aa9ccSJakub Sitnicki 	new_array = bpf_prog_array_alloc(cnt, GFP_KERNEL);
136ce3aa9ccSJakub Sitnicki 	if (!new_array) {
137ce3aa9ccSJakub Sitnicki 		WARN_ON(bpf_prog_array_delete_safe_at(old_array, idx));
138ce3aa9ccSJakub Sitnicki 		goto out_unlock;
139ce3aa9ccSJakub Sitnicki 	}
140ce3aa9ccSJakub Sitnicki 	fill_prog_array(net, type, new_array);
141ce3aa9ccSJakub Sitnicki 	rcu_assign_pointer(net->bpf.run_array[type], new_array);
142ce3aa9ccSJakub Sitnicki 	bpf_prog_array_free(old_array);
143ce3aa9ccSJakub Sitnicki 
1447f045a49SJakub Sitnicki out_unlock:
1457f045a49SJakub Sitnicki 	mutex_unlock(&netns_bpf_mutex);
1467f045a49SJakub Sitnicki }
1477f045a49SJakub Sitnicki 
1487f045a49SJakub Sitnicki static void bpf_netns_link_dealloc(struct bpf_link *link)
1497f045a49SJakub Sitnicki {
1507f045a49SJakub Sitnicki 	struct bpf_netns_link *net_link =
1517f045a49SJakub Sitnicki 		container_of(link, struct bpf_netns_link, link);
1527f045a49SJakub Sitnicki 
1537f045a49SJakub Sitnicki 	kfree(net_link);
1547f045a49SJakub Sitnicki }
1557f045a49SJakub Sitnicki 
1567f045a49SJakub Sitnicki static int bpf_netns_link_update_prog(struct bpf_link *link,
1577f045a49SJakub Sitnicki 				      struct bpf_prog *new_prog,
1587f045a49SJakub Sitnicki 				      struct bpf_prog *old_prog)
1597f045a49SJakub Sitnicki {
1607f045a49SJakub Sitnicki 	struct bpf_netns_link *net_link =
1617f045a49SJakub Sitnicki 		container_of(link, struct bpf_netns_link, link);
1627f045a49SJakub Sitnicki 	enum netns_bpf_attach_type type = net_link->netns_type;
163695c1214SJakub Sitnicki 	struct bpf_prog_array *run_array;
1647f045a49SJakub Sitnicki 	struct net *net;
165ce3aa9ccSJakub Sitnicki 	int idx, ret;
1667f045a49SJakub Sitnicki 
1677f045a49SJakub Sitnicki 	if (old_prog && old_prog != link->prog)
1687f045a49SJakub Sitnicki 		return -EPERM;
1697f045a49SJakub Sitnicki 	if (new_prog->type != link->prog->type)
1707f045a49SJakub Sitnicki 		return -EINVAL;
1717f045a49SJakub Sitnicki 
1727f045a49SJakub Sitnicki 	mutex_lock(&netns_bpf_mutex);
1737f045a49SJakub Sitnicki 
1747f045a49SJakub Sitnicki 	net = net_link->net;
1757f045a49SJakub Sitnicki 	if (!net || !check_net(net)) {
1767f045a49SJakub Sitnicki 		/* Link auto-detached or netns dying */
1777f045a49SJakub Sitnicki 		ret = -ENOLINK;
1787f045a49SJakub Sitnicki 		goto out_unlock;
1797f045a49SJakub Sitnicki 	}
1807f045a49SJakub Sitnicki 
181695c1214SJakub Sitnicki 	run_array = rcu_dereference_protected(net->bpf.run_array[type],
182695c1214SJakub Sitnicki 					      lockdep_is_held(&netns_bpf_mutex));
183ce3aa9ccSJakub Sitnicki 	idx = link_index(net, type, net_link);
184ce3aa9ccSJakub Sitnicki 	ret = bpf_prog_array_update_at(run_array, idx, new_prog);
185ce3aa9ccSJakub Sitnicki 	if (ret)
186ce3aa9ccSJakub Sitnicki 		goto out_unlock;
187695c1214SJakub Sitnicki 
1887f045a49SJakub Sitnicki 	old_prog = xchg(&link->prog, new_prog);
1897f045a49SJakub Sitnicki 	bpf_prog_put(old_prog);
1907f045a49SJakub Sitnicki 
1917f045a49SJakub Sitnicki out_unlock:
1927f045a49SJakub Sitnicki 	mutex_unlock(&netns_bpf_mutex);
1937f045a49SJakub Sitnicki 	return ret;
1947f045a49SJakub Sitnicki }
1957f045a49SJakub Sitnicki 
1967f045a49SJakub Sitnicki static int bpf_netns_link_fill_info(const struct bpf_link *link,
1977f045a49SJakub Sitnicki 				    struct bpf_link_info *info)
1987f045a49SJakub Sitnicki {
1997f045a49SJakub Sitnicki 	const struct bpf_netns_link *net_link =
2007f045a49SJakub Sitnicki 		container_of(link, struct bpf_netns_link, link);
2017f045a49SJakub Sitnicki 	unsigned int inum = 0;
2027f045a49SJakub Sitnicki 	struct net *net;
2037f045a49SJakub Sitnicki 
2047f045a49SJakub Sitnicki 	mutex_lock(&netns_bpf_mutex);
2057f045a49SJakub Sitnicki 	net = net_link->net;
2067f045a49SJakub Sitnicki 	if (net && check_net(net))
2077f045a49SJakub Sitnicki 		inum = net->ns.inum;
2087f045a49SJakub Sitnicki 	mutex_unlock(&netns_bpf_mutex);
2097f045a49SJakub Sitnicki 
2107f045a49SJakub Sitnicki 	info->netns.netns_ino = inum;
2117f045a49SJakub Sitnicki 	info->netns.attach_type = net_link->type;
2127f045a49SJakub Sitnicki 	return 0;
2137f045a49SJakub Sitnicki }
2147f045a49SJakub Sitnicki 
2157f045a49SJakub Sitnicki static void bpf_netns_link_show_fdinfo(const struct bpf_link *link,
2167f045a49SJakub Sitnicki 				       struct seq_file *seq)
2177f045a49SJakub Sitnicki {
2187f045a49SJakub Sitnicki 	struct bpf_link_info info = {};
2197f045a49SJakub Sitnicki 
2207f045a49SJakub Sitnicki 	bpf_netns_link_fill_info(link, &info);
2217f045a49SJakub Sitnicki 	seq_printf(seq,
2227f045a49SJakub Sitnicki 		   "netns_ino:\t%u\n"
2237f045a49SJakub Sitnicki 		   "attach_type:\t%u\n",
2247f045a49SJakub Sitnicki 		   info.netns.netns_ino,
2257f045a49SJakub Sitnicki 		   info.netns.attach_type);
2267f045a49SJakub Sitnicki }
2277f045a49SJakub Sitnicki 
2287f045a49SJakub Sitnicki static const struct bpf_link_ops bpf_netns_link_ops = {
2297f045a49SJakub Sitnicki 	.release = bpf_netns_link_release,
2307f045a49SJakub Sitnicki 	.dealloc = bpf_netns_link_dealloc,
2317f045a49SJakub Sitnicki 	.update_prog = bpf_netns_link_update_prog,
2327f045a49SJakub Sitnicki 	.fill_link_info = bpf_netns_link_fill_info,
2337f045a49SJakub Sitnicki 	.show_fdinfo = bpf_netns_link_show_fdinfo,
2347f045a49SJakub Sitnicki };
2357f045a49SJakub Sitnicki 
236695c1214SJakub Sitnicki /* Must be called with netns_bpf_mutex held. */
237695c1214SJakub Sitnicki static int __netns_bpf_prog_query(const union bpf_attr *attr,
238695c1214SJakub Sitnicki 				  union bpf_attr __user *uattr,
239695c1214SJakub Sitnicki 				  struct net *net,
240695c1214SJakub Sitnicki 				  enum netns_bpf_attach_type type)
241695c1214SJakub Sitnicki {
242695c1214SJakub Sitnicki 	__u32 __user *prog_ids = u64_to_user_ptr(attr->query.prog_ids);
243695c1214SJakub Sitnicki 	struct bpf_prog_array *run_array;
244695c1214SJakub Sitnicki 	u32 prog_cnt = 0, flags = 0;
245695c1214SJakub Sitnicki 
246695c1214SJakub Sitnicki 	run_array = rcu_dereference_protected(net->bpf.run_array[type],
247695c1214SJakub Sitnicki 					      lockdep_is_held(&netns_bpf_mutex));
248695c1214SJakub Sitnicki 	if (run_array)
249695c1214SJakub Sitnicki 		prog_cnt = bpf_prog_array_length(run_array);
250695c1214SJakub Sitnicki 
251695c1214SJakub Sitnicki 	if (copy_to_user(&uattr->query.attach_flags, &flags, sizeof(flags)))
252695c1214SJakub Sitnicki 		return -EFAULT;
253695c1214SJakub Sitnicki 	if (copy_to_user(&uattr->query.prog_cnt, &prog_cnt, sizeof(prog_cnt)))
254695c1214SJakub Sitnicki 		return -EFAULT;
255695c1214SJakub Sitnicki 	if (!attr->query.prog_cnt || !prog_ids || !prog_cnt)
256695c1214SJakub Sitnicki 		return 0;
257695c1214SJakub Sitnicki 
258695c1214SJakub Sitnicki 	return bpf_prog_array_copy_to_user(run_array, prog_ids,
259695c1214SJakub Sitnicki 					   attr->query.prog_cnt);
260695c1214SJakub Sitnicki }
261695c1214SJakub Sitnicki 
262b27f7bb5SJakub Sitnicki int netns_bpf_prog_query(const union bpf_attr *attr,
263b27f7bb5SJakub Sitnicki 			 union bpf_attr __user *uattr)
264b27f7bb5SJakub Sitnicki {
265b27f7bb5SJakub Sitnicki 	enum netns_bpf_attach_type type;
266b27f7bb5SJakub Sitnicki 	struct net *net;
267695c1214SJakub Sitnicki 	int ret;
268b27f7bb5SJakub Sitnicki 
269b27f7bb5SJakub Sitnicki 	if (attr->query.query_flags)
270b27f7bb5SJakub Sitnicki 		return -EINVAL;
271b27f7bb5SJakub Sitnicki 
272b27f7bb5SJakub Sitnicki 	type = to_netns_bpf_attach_type(attr->query.attach_type);
273b27f7bb5SJakub Sitnicki 	if (type < 0)
274b27f7bb5SJakub Sitnicki 		return -EINVAL;
275b27f7bb5SJakub Sitnicki 
276b27f7bb5SJakub Sitnicki 	net = get_net_ns_by_fd(attr->query.target_fd);
277b27f7bb5SJakub Sitnicki 	if (IS_ERR(net))
278b27f7bb5SJakub Sitnicki 		return PTR_ERR(net);
279b27f7bb5SJakub Sitnicki 
280695c1214SJakub Sitnicki 	mutex_lock(&netns_bpf_mutex);
281695c1214SJakub Sitnicki 	ret = __netns_bpf_prog_query(attr, uattr, net, type);
282695c1214SJakub Sitnicki 	mutex_unlock(&netns_bpf_mutex);
283b27f7bb5SJakub Sitnicki 
284b27f7bb5SJakub Sitnicki 	put_net(net);
285695c1214SJakub Sitnicki 	return ret;
286b27f7bb5SJakub Sitnicki }
287b27f7bb5SJakub Sitnicki 
288b27f7bb5SJakub Sitnicki int netns_bpf_prog_attach(const union bpf_attr *attr, struct bpf_prog *prog)
289b27f7bb5SJakub Sitnicki {
290695c1214SJakub Sitnicki 	struct bpf_prog_array *run_array;
291b27f7bb5SJakub Sitnicki 	enum netns_bpf_attach_type type;
2923b701699SJakub Sitnicki 	struct bpf_prog *attached;
293b27f7bb5SJakub Sitnicki 	struct net *net;
294b27f7bb5SJakub Sitnicki 	int ret;
295b27f7bb5SJakub Sitnicki 
2961b514239SLorenz Bauer 	if (attr->target_fd || attr->attach_flags || attr->replace_bpf_fd)
2971b514239SLorenz Bauer 		return -EINVAL;
2981b514239SLorenz Bauer 
299b27f7bb5SJakub Sitnicki 	type = to_netns_bpf_attach_type(attr->attach_type);
300b27f7bb5SJakub Sitnicki 	if (type < 0)
301b27f7bb5SJakub Sitnicki 		return -EINVAL;
302b27f7bb5SJakub Sitnicki 
303b27f7bb5SJakub Sitnicki 	net = current->nsproxy->net_ns;
304b27f7bb5SJakub Sitnicki 	mutex_lock(&netns_bpf_mutex);
3057f045a49SJakub Sitnicki 
3067f045a49SJakub Sitnicki 	/* Attaching prog directly is not compatible with links */
307ab53cad9SJakub Sitnicki 	if (!list_empty(&net->bpf.links[type])) {
3087f045a49SJakub Sitnicki 		ret = -EEXIST;
3097f045a49SJakub Sitnicki 		goto out_unlock;
3107f045a49SJakub Sitnicki 	}
3117f045a49SJakub Sitnicki 
312b27f7bb5SJakub Sitnicki 	switch (type) {
313b27f7bb5SJakub Sitnicki 	case NETNS_BPF_FLOW_DISSECTOR:
3143b701699SJakub Sitnicki 		ret = flow_dissector_bpf_prog_attach_check(net, prog);
315b27f7bb5SJakub Sitnicki 		break;
316b27f7bb5SJakub Sitnicki 	default:
317b27f7bb5SJakub Sitnicki 		ret = -EINVAL;
318b27f7bb5SJakub Sitnicki 		break;
319b27f7bb5SJakub Sitnicki 	}
3203b701699SJakub Sitnicki 	if (ret)
3213b701699SJakub Sitnicki 		goto out_unlock;
3223b701699SJakub Sitnicki 
323695c1214SJakub Sitnicki 	attached = net->bpf.progs[type];
3243b701699SJakub Sitnicki 	if (attached == prog) {
3253b701699SJakub Sitnicki 		/* The same program cannot be attached twice */
3263b701699SJakub Sitnicki 		ret = -EINVAL;
3273b701699SJakub Sitnicki 		goto out_unlock;
3283b701699SJakub Sitnicki 	}
329695c1214SJakub Sitnicki 
330695c1214SJakub Sitnicki 	run_array = rcu_dereference_protected(net->bpf.run_array[type],
331695c1214SJakub Sitnicki 					      lockdep_is_held(&netns_bpf_mutex));
332695c1214SJakub Sitnicki 	if (run_array) {
333695c1214SJakub Sitnicki 		WRITE_ONCE(run_array->items[0].prog, prog);
334695c1214SJakub Sitnicki 	} else {
335695c1214SJakub Sitnicki 		run_array = bpf_prog_array_alloc(1, GFP_KERNEL);
336695c1214SJakub Sitnicki 		if (!run_array) {
337695c1214SJakub Sitnicki 			ret = -ENOMEM;
338695c1214SJakub Sitnicki 			goto out_unlock;
339695c1214SJakub Sitnicki 		}
340695c1214SJakub Sitnicki 		run_array->items[0].prog = prog;
341695c1214SJakub Sitnicki 		rcu_assign_pointer(net->bpf.run_array[type], run_array);
342695c1214SJakub Sitnicki 	}
343695c1214SJakub Sitnicki 
344695c1214SJakub Sitnicki 	net->bpf.progs[type] = prog;
3453b701699SJakub Sitnicki 	if (attached)
3463b701699SJakub Sitnicki 		bpf_prog_put(attached);
3473b701699SJakub Sitnicki 
3487f045a49SJakub Sitnicki out_unlock:
349b27f7bb5SJakub Sitnicki 	mutex_unlock(&netns_bpf_mutex);
350b27f7bb5SJakub Sitnicki 
351b27f7bb5SJakub Sitnicki 	return ret;
352b27f7bb5SJakub Sitnicki }
353b27f7bb5SJakub Sitnicki 
354b27f7bb5SJakub Sitnicki /* Must be called with netns_bpf_mutex held. */
355b27f7bb5SJakub Sitnicki static int __netns_bpf_prog_detach(struct net *net,
3564ac2add6SLorenz Bauer 				   enum netns_bpf_attach_type type,
3574ac2add6SLorenz Bauer 				   struct bpf_prog *old)
358b27f7bb5SJakub Sitnicki {
359b27f7bb5SJakub Sitnicki 	struct bpf_prog *attached;
360b27f7bb5SJakub Sitnicki 
3617f045a49SJakub Sitnicki 	/* Progs attached via links cannot be detached */
362ab53cad9SJakub Sitnicki 	if (!list_empty(&net->bpf.links[type]))
3637f045a49SJakub Sitnicki 		return -EINVAL;
3647f045a49SJakub Sitnicki 
365695c1214SJakub Sitnicki 	attached = net->bpf.progs[type];
3664ac2add6SLorenz Bauer 	if (!attached || attached != old)
367b27f7bb5SJakub Sitnicki 		return -ENOENT;
368695c1214SJakub Sitnicki 	netns_bpf_run_array_detach(net, type);
369695c1214SJakub Sitnicki 	net->bpf.progs[type] = NULL;
370b27f7bb5SJakub Sitnicki 	bpf_prog_put(attached);
371b27f7bb5SJakub Sitnicki 	return 0;
372b27f7bb5SJakub Sitnicki }
373b27f7bb5SJakub Sitnicki 
3744ac2add6SLorenz Bauer int netns_bpf_prog_detach(const union bpf_attr *attr, enum bpf_prog_type ptype)
375b27f7bb5SJakub Sitnicki {
376b27f7bb5SJakub Sitnicki 	enum netns_bpf_attach_type type;
3774ac2add6SLorenz Bauer 	struct bpf_prog *prog;
378b27f7bb5SJakub Sitnicki 	int ret;
379b27f7bb5SJakub Sitnicki 
3804ac2add6SLorenz Bauer 	if (attr->target_fd)
3814ac2add6SLorenz Bauer 		return -EINVAL;
3824ac2add6SLorenz Bauer 
383b27f7bb5SJakub Sitnicki 	type = to_netns_bpf_attach_type(attr->attach_type);
384b27f7bb5SJakub Sitnicki 	if (type < 0)
385b27f7bb5SJakub Sitnicki 		return -EINVAL;
386b27f7bb5SJakub Sitnicki 
3874ac2add6SLorenz Bauer 	prog = bpf_prog_get_type(attr->attach_bpf_fd, ptype);
3884ac2add6SLorenz Bauer 	if (IS_ERR(prog))
3894ac2add6SLorenz Bauer 		return PTR_ERR(prog);
3904ac2add6SLorenz Bauer 
391b27f7bb5SJakub Sitnicki 	mutex_lock(&netns_bpf_mutex);
3924ac2add6SLorenz Bauer 	ret = __netns_bpf_prog_detach(current->nsproxy->net_ns, type, prog);
393b27f7bb5SJakub Sitnicki 	mutex_unlock(&netns_bpf_mutex);
394b27f7bb5SJakub Sitnicki 
3954ac2add6SLorenz Bauer 	bpf_prog_put(prog);
3964ac2add6SLorenz Bauer 
397b27f7bb5SJakub Sitnicki 	return ret;
398b27f7bb5SJakub Sitnicki }
399b27f7bb5SJakub Sitnicki 
400ce3aa9ccSJakub Sitnicki static int netns_bpf_max_progs(enum netns_bpf_attach_type type)
401ce3aa9ccSJakub Sitnicki {
402ce3aa9ccSJakub Sitnicki 	switch (type) {
403ce3aa9ccSJakub Sitnicki 	case NETNS_BPF_FLOW_DISSECTOR:
404ce3aa9ccSJakub Sitnicki 		return 1;
405e9ddbb77SJakub Sitnicki 	case NETNS_BPF_SK_LOOKUP:
406e9ddbb77SJakub Sitnicki 		return 64;
407ce3aa9ccSJakub Sitnicki 	default:
408ce3aa9ccSJakub Sitnicki 		return 0;
409ce3aa9ccSJakub Sitnicki 	}
410ce3aa9ccSJakub Sitnicki }
411ce3aa9ccSJakub Sitnicki 
4127f045a49SJakub Sitnicki static int netns_bpf_link_attach(struct net *net, struct bpf_link *link,
4137f045a49SJakub Sitnicki 				 enum netns_bpf_attach_type type)
4147f045a49SJakub Sitnicki {
415ab53cad9SJakub Sitnicki 	struct bpf_netns_link *net_link =
416ab53cad9SJakub Sitnicki 		container_of(link, struct bpf_netns_link, link);
417695c1214SJakub Sitnicki 	struct bpf_prog_array *run_array;
418ce3aa9ccSJakub Sitnicki 	int cnt, err;
4197f045a49SJakub Sitnicki 
4207f045a49SJakub Sitnicki 	mutex_lock(&netns_bpf_mutex);
4217f045a49SJakub Sitnicki 
422ce3aa9ccSJakub Sitnicki 	cnt = link_count(net, type);
423ce3aa9ccSJakub Sitnicki 	if (cnt >= netns_bpf_max_progs(type)) {
4247f045a49SJakub Sitnicki 		err = -E2BIG;
4257f045a49SJakub Sitnicki 		goto out_unlock;
4267f045a49SJakub Sitnicki 	}
4277f045a49SJakub Sitnicki 	/* Links are not compatible with attaching prog directly */
428695c1214SJakub Sitnicki 	if (net->bpf.progs[type]) {
4297f045a49SJakub Sitnicki 		err = -EEXIST;
4307f045a49SJakub Sitnicki 		goto out_unlock;
4317f045a49SJakub Sitnicki 	}
4327f045a49SJakub Sitnicki 
4337f045a49SJakub Sitnicki 	switch (type) {
4347f045a49SJakub Sitnicki 	case NETNS_BPF_FLOW_DISSECTOR:
4353b701699SJakub Sitnicki 		err = flow_dissector_bpf_prog_attach_check(net, link->prog);
4367f045a49SJakub Sitnicki 		break;
437e9ddbb77SJakub Sitnicki 	case NETNS_BPF_SK_LOOKUP:
438e9ddbb77SJakub Sitnicki 		err = 0; /* nothing to check */
439e9ddbb77SJakub Sitnicki 		break;
4407f045a49SJakub Sitnicki 	default:
4417f045a49SJakub Sitnicki 		err = -EINVAL;
4427f045a49SJakub Sitnicki 		break;
4437f045a49SJakub Sitnicki 	}
4447f045a49SJakub Sitnicki 	if (err)
4457f045a49SJakub Sitnicki 		goto out_unlock;
4467f045a49SJakub Sitnicki 
447ce3aa9ccSJakub Sitnicki 	run_array = bpf_prog_array_alloc(cnt + 1, GFP_KERNEL);
448695c1214SJakub Sitnicki 	if (!run_array) {
449695c1214SJakub Sitnicki 		err = -ENOMEM;
450695c1214SJakub Sitnicki 		goto out_unlock;
451695c1214SJakub Sitnicki 	}
452695c1214SJakub Sitnicki 
453ab53cad9SJakub Sitnicki 	list_add_tail(&net_link->node, &net->bpf.links[type]);
4547f045a49SJakub Sitnicki 
455ce3aa9ccSJakub Sitnicki 	fill_prog_array(net, type, run_array);
456ce3aa9ccSJakub Sitnicki 	run_array = rcu_replace_pointer(net->bpf.run_array[type], run_array,
457ce3aa9ccSJakub Sitnicki 					lockdep_is_held(&netns_bpf_mutex));
458ce3aa9ccSJakub Sitnicki 	bpf_prog_array_free(run_array);
459ce3aa9ccSJakub Sitnicki 
4601559b4aaSJakub Sitnicki 	/* Mark attach point as used */
4611559b4aaSJakub Sitnicki 	netns_bpf_attach_type_need(type);
4621559b4aaSJakub Sitnicki 
4637f045a49SJakub Sitnicki out_unlock:
4647f045a49SJakub Sitnicki 	mutex_unlock(&netns_bpf_mutex);
4657f045a49SJakub Sitnicki 	return err;
4667f045a49SJakub Sitnicki }
4677f045a49SJakub Sitnicki 
4687f045a49SJakub Sitnicki int netns_bpf_link_create(const union bpf_attr *attr, struct bpf_prog *prog)
4697f045a49SJakub Sitnicki {
4707f045a49SJakub Sitnicki 	enum netns_bpf_attach_type netns_type;
4717f045a49SJakub Sitnicki 	struct bpf_link_primer link_primer;
4727f045a49SJakub Sitnicki 	struct bpf_netns_link *net_link;
4737f045a49SJakub Sitnicki 	enum bpf_attach_type type;
4747f045a49SJakub Sitnicki 	struct net *net;
4757f045a49SJakub Sitnicki 	int err;
4767f045a49SJakub Sitnicki 
4777f045a49SJakub Sitnicki 	if (attr->link_create.flags)
4787f045a49SJakub Sitnicki 		return -EINVAL;
4797f045a49SJakub Sitnicki 
4807f045a49SJakub Sitnicki 	type = attr->link_create.attach_type;
4817f045a49SJakub Sitnicki 	netns_type = to_netns_bpf_attach_type(type);
4827f045a49SJakub Sitnicki 	if (netns_type < 0)
4837f045a49SJakub Sitnicki 		return -EINVAL;
4847f045a49SJakub Sitnicki 
4857f045a49SJakub Sitnicki 	net = get_net_ns_by_fd(attr->link_create.target_fd);
4867f045a49SJakub Sitnicki 	if (IS_ERR(net))
4877f045a49SJakub Sitnicki 		return PTR_ERR(net);
4887f045a49SJakub Sitnicki 
4897f045a49SJakub Sitnicki 	net_link = kzalloc(sizeof(*net_link), GFP_USER);
4907f045a49SJakub Sitnicki 	if (!net_link) {
4917f045a49SJakub Sitnicki 		err = -ENOMEM;
4927f045a49SJakub Sitnicki 		goto out_put_net;
4937f045a49SJakub Sitnicki 	}
4947f045a49SJakub Sitnicki 	bpf_link_init(&net_link->link, BPF_LINK_TYPE_NETNS,
4957f045a49SJakub Sitnicki 		      &bpf_netns_link_ops, prog);
4967f045a49SJakub Sitnicki 	net_link->net = net;
4977f045a49SJakub Sitnicki 	net_link->type = type;
4987f045a49SJakub Sitnicki 	net_link->netns_type = netns_type;
4997f045a49SJakub Sitnicki 
5007f045a49SJakub Sitnicki 	err = bpf_link_prime(&net_link->link, &link_primer);
5017f045a49SJakub Sitnicki 	if (err) {
5027f045a49SJakub Sitnicki 		kfree(net_link);
5037f045a49SJakub Sitnicki 		goto out_put_net;
5047f045a49SJakub Sitnicki 	}
5057f045a49SJakub Sitnicki 
5067f045a49SJakub Sitnicki 	err = netns_bpf_link_attach(net, &net_link->link, netns_type);
5077f045a49SJakub Sitnicki 	if (err) {
5087f045a49SJakub Sitnicki 		bpf_link_cleanup(&link_primer);
5097f045a49SJakub Sitnicki 		goto out_put_net;
5107f045a49SJakub Sitnicki 	}
5117f045a49SJakub Sitnicki 
5127f045a49SJakub Sitnicki 	put_net(net);
5137f045a49SJakub Sitnicki 	return bpf_link_settle(&link_primer);
5147f045a49SJakub Sitnicki 
5157f045a49SJakub Sitnicki out_put_net:
5167f045a49SJakub Sitnicki 	put_net(net);
5177f045a49SJakub Sitnicki 	return err;
5187f045a49SJakub Sitnicki }
5197f045a49SJakub Sitnicki 
520ab53cad9SJakub Sitnicki static int __net_init netns_bpf_pernet_init(struct net *net)
521ab53cad9SJakub Sitnicki {
522ab53cad9SJakub Sitnicki 	int type;
523ab53cad9SJakub Sitnicki 
524ab53cad9SJakub Sitnicki 	for (type = 0; type < MAX_NETNS_BPF_ATTACH_TYPE; type++)
525ab53cad9SJakub Sitnicki 		INIT_LIST_HEAD(&net->bpf.links[type]);
526ab53cad9SJakub Sitnicki 
527ab53cad9SJakub Sitnicki 	return 0;
528ab53cad9SJakub Sitnicki }
529ab53cad9SJakub Sitnicki 
530b27f7bb5SJakub Sitnicki static void __net_exit netns_bpf_pernet_pre_exit(struct net *net)
531b27f7bb5SJakub Sitnicki {
532b27f7bb5SJakub Sitnicki 	enum netns_bpf_attach_type type;
533ab53cad9SJakub Sitnicki 	struct bpf_netns_link *net_link;
534b27f7bb5SJakub Sitnicki 
535b27f7bb5SJakub Sitnicki 	mutex_lock(&netns_bpf_mutex);
5367f045a49SJakub Sitnicki 	for (type = 0; type < MAX_NETNS_BPF_ATTACH_TYPE; type++) {
537695c1214SJakub Sitnicki 		netns_bpf_run_array_detach(net, type);
5381559b4aaSJakub Sitnicki 		list_for_each_entry(net_link, &net->bpf.links[type], node) {
539ab53cad9SJakub Sitnicki 			net_link->net = NULL; /* auto-detach link */
5401559b4aaSJakub Sitnicki 			netns_bpf_attach_type_unneed(type);
5411559b4aaSJakub Sitnicki 		}
542ab53cad9SJakub Sitnicki 		if (net->bpf.progs[type])
543695c1214SJakub Sitnicki 			bpf_prog_put(net->bpf.progs[type]);
5447f045a49SJakub Sitnicki 	}
545b27f7bb5SJakub Sitnicki 	mutex_unlock(&netns_bpf_mutex);
546b27f7bb5SJakub Sitnicki }
547b27f7bb5SJakub Sitnicki 
548b27f7bb5SJakub Sitnicki static struct pernet_operations netns_bpf_pernet_ops __net_initdata = {
549ab53cad9SJakub Sitnicki 	.init = netns_bpf_pernet_init,
550b27f7bb5SJakub Sitnicki 	.pre_exit = netns_bpf_pernet_pre_exit,
551b27f7bb5SJakub Sitnicki };
552b27f7bb5SJakub Sitnicki 
553b27f7bb5SJakub Sitnicki static int __init netns_bpf_init(void)
554b27f7bb5SJakub Sitnicki {
555b27f7bb5SJakub Sitnicki 	return register_pernet_subsys(&netns_bpf_pernet_ops);
556b27f7bb5SJakub Sitnicki }
557b27f7bb5SJakub Sitnicki 
558b27f7bb5SJakub Sitnicki subsys_initcall(netns_bpf_init);
559