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