xref: /openbmc/linux/kernel/bpf/net_namespace.c (revision b27f7bb5)
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 
11b27f7bb5SJakub Sitnicki /* Protects updates to netns_bpf */
12b27f7bb5SJakub Sitnicki DEFINE_MUTEX(netns_bpf_mutex);
13b27f7bb5SJakub Sitnicki 
14b27f7bb5SJakub Sitnicki int netns_bpf_prog_query(const union bpf_attr *attr,
15b27f7bb5SJakub Sitnicki 			 union bpf_attr __user *uattr)
16b27f7bb5SJakub Sitnicki {
17b27f7bb5SJakub Sitnicki 	__u32 __user *prog_ids = u64_to_user_ptr(attr->query.prog_ids);
18b27f7bb5SJakub Sitnicki 	u32 prog_id, prog_cnt = 0, flags = 0;
19b27f7bb5SJakub Sitnicki 	enum netns_bpf_attach_type type;
20b27f7bb5SJakub Sitnicki 	struct bpf_prog *attached;
21b27f7bb5SJakub Sitnicki 	struct net *net;
22b27f7bb5SJakub Sitnicki 
23b27f7bb5SJakub Sitnicki 	if (attr->query.query_flags)
24b27f7bb5SJakub Sitnicki 		return -EINVAL;
25b27f7bb5SJakub Sitnicki 
26b27f7bb5SJakub Sitnicki 	type = to_netns_bpf_attach_type(attr->query.attach_type);
27b27f7bb5SJakub Sitnicki 	if (type < 0)
28b27f7bb5SJakub Sitnicki 		return -EINVAL;
29b27f7bb5SJakub Sitnicki 
30b27f7bb5SJakub Sitnicki 	net = get_net_ns_by_fd(attr->query.target_fd);
31b27f7bb5SJakub Sitnicki 	if (IS_ERR(net))
32b27f7bb5SJakub Sitnicki 		return PTR_ERR(net);
33b27f7bb5SJakub Sitnicki 
34b27f7bb5SJakub Sitnicki 	rcu_read_lock();
35b27f7bb5SJakub Sitnicki 	attached = rcu_dereference(net->bpf.progs[type]);
36b27f7bb5SJakub Sitnicki 	if (attached) {
37b27f7bb5SJakub Sitnicki 		prog_cnt = 1;
38b27f7bb5SJakub Sitnicki 		prog_id = attached->aux->id;
39b27f7bb5SJakub Sitnicki 	}
40b27f7bb5SJakub Sitnicki 	rcu_read_unlock();
41b27f7bb5SJakub Sitnicki 
42b27f7bb5SJakub Sitnicki 	put_net(net);
43b27f7bb5SJakub Sitnicki 
44b27f7bb5SJakub Sitnicki 	if (copy_to_user(&uattr->query.attach_flags, &flags, sizeof(flags)))
45b27f7bb5SJakub Sitnicki 		return -EFAULT;
46b27f7bb5SJakub Sitnicki 	if (copy_to_user(&uattr->query.prog_cnt, &prog_cnt, sizeof(prog_cnt)))
47b27f7bb5SJakub Sitnicki 		return -EFAULT;
48b27f7bb5SJakub Sitnicki 
49b27f7bb5SJakub Sitnicki 	if (!attr->query.prog_cnt || !prog_ids || !prog_cnt)
50b27f7bb5SJakub Sitnicki 		return 0;
51b27f7bb5SJakub Sitnicki 
52b27f7bb5SJakub Sitnicki 	if (copy_to_user(prog_ids, &prog_id, sizeof(u32)))
53b27f7bb5SJakub Sitnicki 		return -EFAULT;
54b27f7bb5SJakub Sitnicki 
55b27f7bb5SJakub Sitnicki 	return 0;
56b27f7bb5SJakub Sitnicki }
57b27f7bb5SJakub Sitnicki 
58b27f7bb5SJakub Sitnicki int netns_bpf_prog_attach(const union bpf_attr *attr, struct bpf_prog *prog)
59b27f7bb5SJakub Sitnicki {
60b27f7bb5SJakub Sitnicki 	enum netns_bpf_attach_type type;
61b27f7bb5SJakub Sitnicki 	struct net *net;
62b27f7bb5SJakub Sitnicki 	int ret;
63b27f7bb5SJakub Sitnicki 
64b27f7bb5SJakub Sitnicki 	type = to_netns_bpf_attach_type(attr->attach_type);
65b27f7bb5SJakub Sitnicki 	if (type < 0)
66b27f7bb5SJakub Sitnicki 		return -EINVAL;
67b27f7bb5SJakub Sitnicki 
68b27f7bb5SJakub Sitnicki 	net = current->nsproxy->net_ns;
69b27f7bb5SJakub Sitnicki 	mutex_lock(&netns_bpf_mutex);
70b27f7bb5SJakub Sitnicki 	switch (type) {
71b27f7bb5SJakub Sitnicki 	case NETNS_BPF_FLOW_DISSECTOR:
72b27f7bb5SJakub Sitnicki 		ret = flow_dissector_bpf_prog_attach(net, prog);
73b27f7bb5SJakub Sitnicki 		break;
74b27f7bb5SJakub Sitnicki 	default:
75b27f7bb5SJakub Sitnicki 		ret = -EINVAL;
76b27f7bb5SJakub Sitnicki 		break;
77b27f7bb5SJakub Sitnicki 	}
78b27f7bb5SJakub Sitnicki 	mutex_unlock(&netns_bpf_mutex);
79b27f7bb5SJakub Sitnicki 
80b27f7bb5SJakub Sitnicki 	return ret;
81b27f7bb5SJakub Sitnicki }
82b27f7bb5SJakub Sitnicki 
83b27f7bb5SJakub Sitnicki /* Must be called with netns_bpf_mutex held. */
84b27f7bb5SJakub Sitnicki static int __netns_bpf_prog_detach(struct net *net,
85b27f7bb5SJakub Sitnicki 				   enum netns_bpf_attach_type type)
86b27f7bb5SJakub Sitnicki {
87b27f7bb5SJakub Sitnicki 	struct bpf_prog *attached;
88b27f7bb5SJakub Sitnicki 
89b27f7bb5SJakub Sitnicki 	attached = rcu_dereference_protected(net->bpf.progs[type],
90b27f7bb5SJakub Sitnicki 					     lockdep_is_held(&netns_bpf_mutex));
91b27f7bb5SJakub Sitnicki 	if (!attached)
92b27f7bb5SJakub Sitnicki 		return -ENOENT;
93b27f7bb5SJakub Sitnicki 	RCU_INIT_POINTER(net->bpf.progs[type], NULL);
94b27f7bb5SJakub Sitnicki 	bpf_prog_put(attached);
95b27f7bb5SJakub Sitnicki 	return 0;
96b27f7bb5SJakub Sitnicki }
97b27f7bb5SJakub Sitnicki 
98b27f7bb5SJakub Sitnicki int netns_bpf_prog_detach(const union bpf_attr *attr)
99b27f7bb5SJakub Sitnicki {
100b27f7bb5SJakub Sitnicki 	enum netns_bpf_attach_type type;
101b27f7bb5SJakub Sitnicki 	int ret;
102b27f7bb5SJakub Sitnicki 
103b27f7bb5SJakub Sitnicki 	type = to_netns_bpf_attach_type(attr->attach_type);
104b27f7bb5SJakub Sitnicki 	if (type < 0)
105b27f7bb5SJakub Sitnicki 		return -EINVAL;
106b27f7bb5SJakub Sitnicki 
107b27f7bb5SJakub Sitnicki 	mutex_lock(&netns_bpf_mutex);
108b27f7bb5SJakub Sitnicki 	ret = __netns_bpf_prog_detach(current->nsproxy->net_ns, type);
109b27f7bb5SJakub Sitnicki 	mutex_unlock(&netns_bpf_mutex);
110b27f7bb5SJakub Sitnicki 
111b27f7bb5SJakub Sitnicki 	return ret;
112b27f7bb5SJakub Sitnicki }
113b27f7bb5SJakub Sitnicki 
114b27f7bb5SJakub Sitnicki static void __net_exit netns_bpf_pernet_pre_exit(struct net *net)
115b27f7bb5SJakub Sitnicki {
116b27f7bb5SJakub Sitnicki 	enum netns_bpf_attach_type type;
117b27f7bb5SJakub Sitnicki 
118b27f7bb5SJakub Sitnicki 	mutex_lock(&netns_bpf_mutex);
119b27f7bb5SJakub Sitnicki 	for (type = 0; type < MAX_NETNS_BPF_ATTACH_TYPE; type++)
120b27f7bb5SJakub Sitnicki 		__netns_bpf_prog_detach(net, type);
121b27f7bb5SJakub Sitnicki 	mutex_unlock(&netns_bpf_mutex);
122b27f7bb5SJakub Sitnicki }
123b27f7bb5SJakub Sitnicki 
124b27f7bb5SJakub Sitnicki static struct pernet_operations netns_bpf_pernet_ops __net_initdata = {
125b27f7bb5SJakub Sitnicki 	.pre_exit = netns_bpf_pernet_pre_exit,
126b27f7bb5SJakub Sitnicki };
127b27f7bb5SJakub Sitnicki 
128b27f7bb5SJakub Sitnicki static int __init netns_bpf_init(void)
129b27f7bb5SJakub Sitnicki {
130b27f7bb5SJakub Sitnicki 	return register_pernet_subsys(&netns_bpf_pernet_ops);
131b27f7bb5SJakub Sitnicki }
132b27f7bb5SJakub Sitnicki 
133b27f7bb5SJakub Sitnicki subsys_initcall(netns_bpf_init);
134