xref: /openbmc/linux/kernel/bpf/net_namespace.c (revision 3b701699)
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;
227f045a49SJakub Sitnicki };
237f045a49SJakub Sitnicki 
24b27f7bb5SJakub Sitnicki /* Protects updates to netns_bpf */
25b27f7bb5SJakub Sitnicki DEFINE_MUTEX(netns_bpf_mutex);
26b27f7bb5SJakub Sitnicki 
277f045a49SJakub Sitnicki /* Must be called with netns_bpf_mutex held. */
287f045a49SJakub Sitnicki static void __net_exit bpf_netns_link_auto_detach(struct bpf_link *link)
297f045a49SJakub Sitnicki {
307f045a49SJakub Sitnicki 	struct bpf_netns_link *net_link =
317f045a49SJakub Sitnicki 		container_of(link, struct bpf_netns_link, link);
327f045a49SJakub Sitnicki 
337f045a49SJakub Sitnicki 	net_link->net = NULL;
347f045a49SJakub Sitnicki }
357f045a49SJakub Sitnicki 
367f045a49SJakub Sitnicki static void bpf_netns_link_release(struct bpf_link *link)
377f045a49SJakub Sitnicki {
387f045a49SJakub Sitnicki 	struct bpf_netns_link *net_link =
397f045a49SJakub Sitnicki 		container_of(link, struct bpf_netns_link, link);
407f045a49SJakub Sitnicki 	enum netns_bpf_attach_type type = net_link->netns_type;
417f045a49SJakub Sitnicki 	struct net *net;
427f045a49SJakub Sitnicki 
437f045a49SJakub Sitnicki 	/* Link auto-detached by dying netns. */
447f045a49SJakub Sitnicki 	if (!net_link->net)
457f045a49SJakub Sitnicki 		return;
467f045a49SJakub Sitnicki 
477f045a49SJakub Sitnicki 	mutex_lock(&netns_bpf_mutex);
487f045a49SJakub Sitnicki 
497f045a49SJakub Sitnicki 	/* Recheck after potential sleep. We can race with cleanup_net
507f045a49SJakub Sitnicki 	 * here, but if we see a non-NULL struct net pointer pre_exit
517f045a49SJakub Sitnicki 	 * has not happened yet and will block on netns_bpf_mutex.
527f045a49SJakub Sitnicki 	 */
537f045a49SJakub Sitnicki 	net = net_link->net;
547f045a49SJakub Sitnicki 	if (!net)
557f045a49SJakub Sitnicki 		goto out_unlock;
567f045a49SJakub Sitnicki 
577f045a49SJakub Sitnicki 	net->bpf.links[type] = NULL;
587f045a49SJakub Sitnicki 	RCU_INIT_POINTER(net->bpf.progs[type], NULL);
597f045a49SJakub Sitnicki 
607f045a49SJakub Sitnicki out_unlock:
617f045a49SJakub Sitnicki 	mutex_unlock(&netns_bpf_mutex);
627f045a49SJakub Sitnicki }
637f045a49SJakub Sitnicki 
647f045a49SJakub Sitnicki static void bpf_netns_link_dealloc(struct bpf_link *link)
657f045a49SJakub Sitnicki {
667f045a49SJakub Sitnicki 	struct bpf_netns_link *net_link =
677f045a49SJakub Sitnicki 		container_of(link, struct bpf_netns_link, link);
687f045a49SJakub Sitnicki 
697f045a49SJakub Sitnicki 	kfree(net_link);
707f045a49SJakub Sitnicki }
717f045a49SJakub Sitnicki 
727f045a49SJakub Sitnicki static int bpf_netns_link_update_prog(struct bpf_link *link,
737f045a49SJakub Sitnicki 				      struct bpf_prog *new_prog,
747f045a49SJakub Sitnicki 				      struct bpf_prog *old_prog)
757f045a49SJakub Sitnicki {
767f045a49SJakub Sitnicki 	struct bpf_netns_link *net_link =
777f045a49SJakub Sitnicki 		container_of(link, struct bpf_netns_link, link);
787f045a49SJakub Sitnicki 	enum netns_bpf_attach_type type = net_link->netns_type;
797f045a49SJakub Sitnicki 	struct net *net;
807f045a49SJakub Sitnicki 	int ret = 0;
817f045a49SJakub Sitnicki 
827f045a49SJakub Sitnicki 	if (old_prog && old_prog != link->prog)
837f045a49SJakub Sitnicki 		return -EPERM;
847f045a49SJakub Sitnicki 	if (new_prog->type != link->prog->type)
857f045a49SJakub Sitnicki 		return -EINVAL;
867f045a49SJakub Sitnicki 
877f045a49SJakub Sitnicki 	mutex_lock(&netns_bpf_mutex);
887f045a49SJakub Sitnicki 
897f045a49SJakub Sitnicki 	net = net_link->net;
907f045a49SJakub Sitnicki 	if (!net || !check_net(net)) {
917f045a49SJakub Sitnicki 		/* Link auto-detached or netns dying */
927f045a49SJakub Sitnicki 		ret = -ENOLINK;
937f045a49SJakub Sitnicki 		goto out_unlock;
947f045a49SJakub Sitnicki 	}
957f045a49SJakub Sitnicki 
967f045a49SJakub Sitnicki 	old_prog = xchg(&link->prog, new_prog);
977f045a49SJakub Sitnicki 	rcu_assign_pointer(net->bpf.progs[type], new_prog);
987f045a49SJakub Sitnicki 	bpf_prog_put(old_prog);
997f045a49SJakub Sitnicki 
1007f045a49SJakub Sitnicki out_unlock:
1017f045a49SJakub Sitnicki 	mutex_unlock(&netns_bpf_mutex);
1027f045a49SJakub Sitnicki 	return ret;
1037f045a49SJakub Sitnicki }
1047f045a49SJakub Sitnicki 
1057f045a49SJakub Sitnicki static int bpf_netns_link_fill_info(const struct bpf_link *link,
1067f045a49SJakub Sitnicki 				    struct bpf_link_info *info)
1077f045a49SJakub Sitnicki {
1087f045a49SJakub Sitnicki 	const struct bpf_netns_link *net_link =
1097f045a49SJakub Sitnicki 		container_of(link, struct bpf_netns_link, link);
1107f045a49SJakub Sitnicki 	unsigned int inum = 0;
1117f045a49SJakub Sitnicki 	struct net *net;
1127f045a49SJakub Sitnicki 
1137f045a49SJakub Sitnicki 	mutex_lock(&netns_bpf_mutex);
1147f045a49SJakub Sitnicki 	net = net_link->net;
1157f045a49SJakub Sitnicki 	if (net && check_net(net))
1167f045a49SJakub Sitnicki 		inum = net->ns.inum;
1177f045a49SJakub Sitnicki 	mutex_unlock(&netns_bpf_mutex);
1187f045a49SJakub Sitnicki 
1197f045a49SJakub Sitnicki 	info->netns.netns_ino = inum;
1207f045a49SJakub Sitnicki 	info->netns.attach_type = net_link->type;
1217f045a49SJakub Sitnicki 	return 0;
1227f045a49SJakub Sitnicki }
1237f045a49SJakub Sitnicki 
1247f045a49SJakub Sitnicki static void bpf_netns_link_show_fdinfo(const struct bpf_link *link,
1257f045a49SJakub Sitnicki 				       struct seq_file *seq)
1267f045a49SJakub Sitnicki {
1277f045a49SJakub Sitnicki 	struct bpf_link_info info = {};
1287f045a49SJakub Sitnicki 
1297f045a49SJakub Sitnicki 	bpf_netns_link_fill_info(link, &info);
1307f045a49SJakub Sitnicki 	seq_printf(seq,
1317f045a49SJakub Sitnicki 		   "netns_ino:\t%u\n"
1327f045a49SJakub Sitnicki 		   "attach_type:\t%u\n",
1337f045a49SJakub Sitnicki 		   info.netns.netns_ino,
1347f045a49SJakub Sitnicki 		   info.netns.attach_type);
1357f045a49SJakub Sitnicki }
1367f045a49SJakub Sitnicki 
1377f045a49SJakub Sitnicki static const struct bpf_link_ops bpf_netns_link_ops = {
1387f045a49SJakub Sitnicki 	.release = bpf_netns_link_release,
1397f045a49SJakub Sitnicki 	.dealloc = bpf_netns_link_dealloc,
1407f045a49SJakub Sitnicki 	.update_prog = bpf_netns_link_update_prog,
1417f045a49SJakub Sitnicki 	.fill_link_info = bpf_netns_link_fill_info,
1427f045a49SJakub Sitnicki 	.show_fdinfo = bpf_netns_link_show_fdinfo,
1437f045a49SJakub Sitnicki };
1447f045a49SJakub Sitnicki 
145b27f7bb5SJakub Sitnicki int netns_bpf_prog_query(const union bpf_attr *attr,
146b27f7bb5SJakub Sitnicki 			 union bpf_attr __user *uattr)
147b27f7bb5SJakub Sitnicki {
148b27f7bb5SJakub Sitnicki 	__u32 __user *prog_ids = u64_to_user_ptr(attr->query.prog_ids);
149b27f7bb5SJakub Sitnicki 	u32 prog_id, prog_cnt = 0, flags = 0;
150b27f7bb5SJakub Sitnicki 	enum netns_bpf_attach_type type;
151b27f7bb5SJakub Sitnicki 	struct bpf_prog *attached;
152b27f7bb5SJakub Sitnicki 	struct net *net;
153b27f7bb5SJakub Sitnicki 
154b27f7bb5SJakub Sitnicki 	if (attr->query.query_flags)
155b27f7bb5SJakub Sitnicki 		return -EINVAL;
156b27f7bb5SJakub Sitnicki 
157b27f7bb5SJakub Sitnicki 	type = to_netns_bpf_attach_type(attr->query.attach_type);
158b27f7bb5SJakub Sitnicki 	if (type < 0)
159b27f7bb5SJakub Sitnicki 		return -EINVAL;
160b27f7bb5SJakub Sitnicki 
161b27f7bb5SJakub Sitnicki 	net = get_net_ns_by_fd(attr->query.target_fd);
162b27f7bb5SJakub Sitnicki 	if (IS_ERR(net))
163b27f7bb5SJakub Sitnicki 		return PTR_ERR(net);
164b27f7bb5SJakub Sitnicki 
165b27f7bb5SJakub Sitnicki 	rcu_read_lock();
166b27f7bb5SJakub Sitnicki 	attached = rcu_dereference(net->bpf.progs[type]);
167b27f7bb5SJakub Sitnicki 	if (attached) {
168b27f7bb5SJakub Sitnicki 		prog_cnt = 1;
169b27f7bb5SJakub Sitnicki 		prog_id = attached->aux->id;
170b27f7bb5SJakub Sitnicki 	}
171b27f7bb5SJakub Sitnicki 	rcu_read_unlock();
172b27f7bb5SJakub Sitnicki 
173b27f7bb5SJakub Sitnicki 	put_net(net);
174b27f7bb5SJakub Sitnicki 
175b27f7bb5SJakub Sitnicki 	if (copy_to_user(&uattr->query.attach_flags, &flags, sizeof(flags)))
176b27f7bb5SJakub Sitnicki 		return -EFAULT;
177b27f7bb5SJakub Sitnicki 	if (copy_to_user(&uattr->query.prog_cnt, &prog_cnt, sizeof(prog_cnt)))
178b27f7bb5SJakub Sitnicki 		return -EFAULT;
179b27f7bb5SJakub Sitnicki 
180b27f7bb5SJakub Sitnicki 	if (!attr->query.prog_cnt || !prog_ids || !prog_cnt)
181b27f7bb5SJakub Sitnicki 		return 0;
182b27f7bb5SJakub Sitnicki 
183b27f7bb5SJakub Sitnicki 	if (copy_to_user(prog_ids, &prog_id, sizeof(u32)))
184b27f7bb5SJakub Sitnicki 		return -EFAULT;
185b27f7bb5SJakub Sitnicki 
186b27f7bb5SJakub Sitnicki 	return 0;
187b27f7bb5SJakub Sitnicki }
188b27f7bb5SJakub Sitnicki 
189b27f7bb5SJakub Sitnicki int netns_bpf_prog_attach(const union bpf_attr *attr, struct bpf_prog *prog)
190b27f7bb5SJakub Sitnicki {
191b27f7bb5SJakub Sitnicki 	enum netns_bpf_attach_type type;
1923b701699SJakub Sitnicki 	struct bpf_prog *attached;
193b27f7bb5SJakub Sitnicki 	struct net *net;
194b27f7bb5SJakub Sitnicki 	int ret;
195b27f7bb5SJakub Sitnicki 
196b27f7bb5SJakub Sitnicki 	type = to_netns_bpf_attach_type(attr->attach_type);
197b27f7bb5SJakub Sitnicki 	if (type < 0)
198b27f7bb5SJakub Sitnicki 		return -EINVAL;
199b27f7bb5SJakub Sitnicki 
200b27f7bb5SJakub Sitnicki 	net = current->nsproxy->net_ns;
201b27f7bb5SJakub Sitnicki 	mutex_lock(&netns_bpf_mutex);
2027f045a49SJakub Sitnicki 
2037f045a49SJakub Sitnicki 	/* Attaching prog directly is not compatible with links */
2047f045a49SJakub Sitnicki 	if (net->bpf.links[type]) {
2057f045a49SJakub Sitnicki 		ret = -EEXIST;
2067f045a49SJakub Sitnicki 		goto out_unlock;
2077f045a49SJakub Sitnicki 	}
2087f045a49SJakub Sitnicki 
209b27f7bb5SJakub Sitnicki 	switch (type) {
210b27f7bb5SJakub Sitnicki 	case NETNS_BPF_FLOW_DISSECTOR:
2113b701699SJakub Sitnicki 		ret = flow_dissector_bpf_prog_attach_check(net, prog);
212b27f7bb5SJakub Sitnicki 		break;
213b27f7bb5SJakub Sitnicki 	default:
214b27f7bb5SJakub Sitnicki 		ret = -EINVAL;
215b27f7bb5SJakub Sitnicki 		break;
216b27f7bb5SJakub Sitnicki 	}
2173b701699SJakub Sitnicki 	if (ret)
2183b701699SJakub Sitnicki 		goto out_unlock;
2193b701699SJakub Sitnicki 
2203b701699SJakub Sitnicki 	attached = rcu_dereference_protected(net->bpf.progs[type],
2213b701699SJakub Sitnicki 					     lockdep_is_held(&netns_bpf_mutex));
2223b701699SJakub Sitnicki 	if (attached == prog) {
2233b701699SJakub Sitnicki 		/* The same program cannot be attached twice */
2243b701699SJakub Sitnicki 		ret = -EINVAL;
2253b701699SJakub Sitnicki 		goto out_unlock;
2263b701699SJakub Sitnicki 	}
2273b701699SJakub Sitnicki 	rcu_assign_pointer(net->bpf.progs[type], prog);
2283b701699SJakub Sitnicki 	if (attached)
2293b701699SJakub Sitnicki 		bpf_prog_put(attached);
2303b701699SJakub Sitnicki 
2317f045a49SJakub Sitnicki out_unlock:
232b27f7bb5SJakub Sitnicki 	mutex_unlock(&netns_bpf_mutex);
233b27f7bb5SJakub Sitnicki 
234b27f7bb5SJakub Sitnicki 	return ret;
235b27f7bb5SJakub Sitnicki }
236b27f7bb5SJakub Sitnicki 
237b27f7bb5SJakub Sitnicki /* Must be called with netns_bpf_mutex held. */
238b27f7bb5SJakub Sitnicki static int __netns_bpf_prog_detach(struct net *net,
239b27f7bb5SJakub Sitnicki 				   enum netns_bpf_attach_type type)
240b27f7bb5SJakub Sitnicki {
241b27f7bb5SJakub Sitnicki 	struct bpf_prog *attached;
242b27f7bb5SJakub Sitnicki 
2437f045a49SJakub Sitnicki 	/* Progs attached via links cannot be detached */
2447f045a49SJakub Sitnicki 	if (net->bpf.links[type])
2457f045a49SJakub Sitnicki 		return -EINVAL;
2467f045a49SJakub Sitnicki 
247b27f7bb5SJakub Sitnicki 	attached = rcu_dereference_protected(net->bpf.progs[type],
248b27f7bb5SJakub Sitnicki 					     lockdep_is_held(&netns_bpf_mutex));
249b27f7bb5SJakub Sitnicki 	if (!attached)
250b27f7bb5SJakub Sitnicki 		return -ENOENT;
251b27f7bb5SJakub Sitnicki 	RCU_INIT_POINTER(net->bpf.progs[type], NULL);
252b27f7bb5SJakub Sitnicki 	bpf_prog_put(attached);
253b27f7bb5SJakub Sitnicki 	return 0;
254b27f7bb5SJakub Sitnicki }
255b27f7bb5SJakub Sitnicki 
256b27f7bb5SJakub Sitnicki int netns_bpf_prog_detach(const union bpf_attr *attr)
257b27f7bb5SJakub Sitnicki {
258b27f7bb5SJakub Sitnicki 	enum netns_bpf_attach_type type;
259b27f7bb5SJakub Sitnicki 	int ret;
260b27f7bb5SJakub Sitnicki 
261b27f7bb5SJakub Sitnicki 	type = to_netns_bpf_attach_type(attr->attach_type);
262b27f7bb5SJakub Sitnicki 	if (type < 0)
263b27f7bb5SJakub Sitnicki 		return -EINVAL;
264b27f7bb5SJakub Sitnicki 
265b27f7bb5SJakub Sitnicki 	mutex_lock(&netns_bpf_mutex);
266b27f7bb5SJakub Sitnicki 	ret = __netns_bpf_prog_detach(current->nsproxy->net_ns, type);
267b27f7bb5SJakub Sitnicki 	mutex_unlock(&netns_bpf_mutex);
268b27f7bb5SJakub Sitnicki 
269b27f7bb5SJakub Sitnicki 	return ret;
270b27f7bb5SJakub Sitnicki }
271b27f7bb5SJakub Sitnicki 
2727f045a49SJakub Sitnicki static int netns_bpf_link_attach(struct net *net, struct bpf_link *link,
2737f045a49SJakub Sitnicki 				 enum netns_bpf_attach_type type)
2747f045a49SJakub Sitnicki {
2757f045a49SJakub Sitnicki 	struct bpf_prog *prog;
2767f045a49SJakub Sitnicki 	int err;
2777f045a49SJakub Sitnicki 
2787f045a49SJakub Sitnicki 	mutex_lock(&netns_bpf_mutex);
2797f045a49SJakub Sitnicki 
2807f045a49SJakub Sitnicki 	/* Allow attaching only one prog or link for now */
2817f045a49SJakub Sitnicki 	if (net->bpf.links[type]) {
2827f045a49SJakub Sitnicki 		err = -E2BIG;
2837f045a49SJakub Sitnicki 		goto out_unlock;
2847f045a49SJakub Sitnicki 	}
2857f045a49SJakub Sitnicki 	/* Links are not compatible with attaching prog directly */
2867f045a49SJakub Sitnicki 	prog = rcu_dereference_protected(net->bpf.progs[type],
2877f045a49SJakub Sitnicki 					 lockdep_is_held(&netns_bpf_mutex));
2887f045a49SJakub Sitnicki 	if (prog) {
2897f045a49SJakub Sitnicki 		err = -EEXIST;
2907f045a49SJakub Sitnicki 		goto out_unlock;
2917f045a49SJakub Sitnicki 	}
2927f045a49SJakub Sitnicki 
2937f045a49SJakub Sitnicki 	switch (type) {
2947f045a49SJakub Sitnicki 	case NETNS_BPF_FLOW_DISSECTOR:
2953b701699SJakub Sitnicki 		err = flow_dissector_bpf_prog_attach_check(net, link->prog);
2967f045a49SJakub Sitnicki 		break;
2977f045a49SJakub Sitnicki 	default:
2987f045a49SJakub Sitnicki 		err = -EINVAL;
2997f045a49SJakub Sitnicki 		break;
3007f045a49SJakub Sitnicki 	}
3017f045a49SJakub Sitnicki 	if (err)
3027f045a49SJakub Sitnicki 		goto out_unlock;
3037f045a49SJakub Sitnicki 
3043b701699SJakub Sitnicki 	rcu_assign_pointer(net->bpf.progs[type], link->prog);
3057f045a49SJakub Sitnicki 	net->bpf.links[type] = link;
3067f045a49SJakub Sitnicki 
3077f045a49SJakub Sitnicki out_unlock:
3087f045a49SJakub Sitnicki 	mutex_unlock(&netns_bpf_mutex);
3097f045a49SJakub Sitnicki 	return err;
3107f045a49SJakub Sitnicki }
3117f045a49SJakub Sitnicki 
3127f045a49SJakub Sitnicki int netns_bpf_link_create(const union bpf_attr *attr, struct bpf_prog *prog)
3137f045a49SJakub Sitnicki {
3147f045a49SJakub Sitnicki 	enum netns_bpf_attach_type netns_type;
3157f045a49SJakub Sitnicki 	struct bpf_link_primer link_primer;
3167f045a49SJakub Sitnicki 	struct bpf_netns_link *net_link;
3177f045a49SJakub Sitnicki 	enum bpf_attach_type type;
3187f045a49SJakub Sitnicki 	struct net *net;
3197f045a49SJakub Sitnicki 	int err;
3207f045a49SJakub Sitnicki 
3217f045a49SJakub Sitnicki 	if (attr->link_create.flags)
3227f045a49SJakub Sitnicki 		return -EINVAL;
3237f045a49SJakub Sitnicki 
3247f045a49SJakub Sitnicki 	type = attr->link_create.attach_type;
3257f045a49SJakub Sitnicki 	netns_type = to_netns_bpf_attach_type(type);
3267f045a49SJakub Sitnicki 	if (netns_type < 0)
3277f045a49SJakub Sitnicki 		return -EINVAL;
3287f045a49SJakub Sitnicki 
3297f045a49SJakub Sitnicki 	net = get_net_ns_by_fd(attr->link_create.target_fd);
3307f045a49SJakub Sitnicki 	if (IS_ERR(net))
3317f045a49SJakub Sitnicki 		return PTR_ERR(net);
3327f045a49SJakub Sitnicki 
3337f045a49SJakub Sitnicki 	net_link = kzalloc(sizeof(*net_link), GFP_USER);
3347f045a49SJakub Sitnicki 	if (!net_link) {
3357f045a49SJakub Sitnicki 		err = -ENOMEM;
3367f045a49SJakub Sitnicki 		goto out_put_net;
3377f045a49SJakub Sitnicki 	}
3387f045a49SJakub Sitnicki 	bpf_link_init(&net_link->link, BPF_LINK_TYPE_NETNS,
3397f045a49SJakub Sitnicki 		      &bpf_netns_link_ops, prog);
3407f045a49SJakub Sitnicki 	net_link->net = net;
3417f045a49SJakub Sitnicki 	net_link->type = type;
3427f045a49SJakub Sitnicki 	net_link->netns_type = netns_type;
3437f045a49SJakub Sitnicki 
3447f045a49SJakub Sitnicki 	err = bpf_link_prime(&net_link->link, &link_primer);
3457f045a49SJakub Sitnicki 	if (err) {
3467f045a49SJakub Sitnicki 		kfree(net_link);
3477f045a49SJakub Sitnicki 		goto out_put_net;
3487f045a49SJakub Sitnicki 	}
3497f045a49SJakub Sitnicki 
3507f045a49SJakub Sitnicki 	err = netns_bpf_link_attach(net, &net_link->link, netns_type);
3517f045a49SJakub Sitnicki 	if (err) {
3527f045a49SJakub Sitnicki 		bpf_link_cleanup(&link_primer);
3537f045a49SJakub Sitnicki 		goto out_put_net;
3547f045a49SJakub Sitnicki 	}
3557f045a49SJakub Sitnicki 
3567f045a49SJakub Sitnicki 	put_net(net);
3577f045a49SJakub Sitnicki 	return bpf_link_settle(&link_primer);
3587f045a49SJakub Sitnicki 
3597f045a49SJakub Sitnicki out_put_net:
3607f045a49SJakub Sitnicki 	put_net(net);
3617f045a49SJakub Sitnicki 	return err;
3627f045a49SJakub Sitnicki }
3637f045a49SJakub Sitnicki 
364b27f7bb5SJakub Sitnicki static void __net_exit netns_bpf_pernet_pre_exit(struct net *net)
365b27f7bb5SJakub Sitnicki {
366b27f7bb5SJakub Sitnicki 	enum netns_bpf_attach_type type;
3677f045a49SJakub Sitnicki 	struct bpf_link *link;
368b27f7bb5SJakub Sitnicki 
369b27f7bb5SJakub Sitnicki 	mutex_lock(&netns_bpf_mutex);
3707f045a49SJakub Sitnicki 	for (type = 0; type < MAX_NETNS_BPF_ATTACH_TYPE; type++) {
3717f045a49SJakub Sitnicki 		link = net->bpf.links[type];
3727f045a49SJakub Sitnicki 		if (link)
3737f045a49SJakub Sitnicki 			bpf_netns_link_auto_detach(link);
3747f045a49SJakub Sitnicki 		else
375b27f7bb5SJakub Sitnicki 			__netns_bpf_prog_detach(net, type);
3767f045a49SJakub Sitnicki 	}
377b27f7bb5SJakub Sitnicki 	mutex_unlock(&netns_bpf_mutex);
378b27f7bb5SJakub Sitnicki }
379b27f7bb5SJakub Sitnicki 
380b27f7bb5SJakub Sitnicki static struct pernet_operations netns_bpf_pernet_ops __net_initdata = {
381b27f7bb5SJakub Sitnicki 	.pre_exit = netns_bpf_pernet_pre_exit,
382b27f7bb5SJakub Sitnicki };
383b27f7bb5SJakub Sitnicki 
384b27f7bb5SJakub Sitnicki static int __init netns_bpf_init(void)
385b27f7bb5SJakub Sitnicki {
386b27f7bb5SJakub Sitnicki 	return register_pernet_subsys(&netns_bpf_pernet_ops);
387b27f7bb5SJakub Sitnicki }
388b27f7bb5SJakub Sitnicki 
389b27f7bb5SJakub Sitnicki subsys_initcall(netns_bpf_init);
390