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