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