cregit-Linux how code gets into the kernel

Release 4.8 net/core/net_namespace.c

Directory: net/core

#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

#include <linux/workqueue.h>
#include <linux/rtnetlink.h>
#include <linux/cache.h>
#include <linux/slab.h>
#include <linux/list.h>
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/idr.h>
#include <linux/rculist.h>
#include <linux/nsproxy.h>
#include <linux/fs.h>
#include <linux/proc_ns.h>
#include <linux/file.h>
#include <linux/export.h>
#include <linux/user_namespace.h>
#include <linux/net_namespace.h>
#include <net/sock.h>
#include <net/netlink.h>
#include <net/net_namespace.h>
#include <net/netns/generic.h>

/*
 *      Our network namespace constructor/destructor lists
 */

static LIST_HEAD(pernet_list);

static struct list_head *first_device = &pernet_list;

DEFINE_MUTEX(net_mutex);


LIST_HEAD(net_namespace_list);

EXPORT_SYMBOL_GPL(net_namespace_list);


struct net init_net = {
	.dev_base_head = LIST_HEAD_INIT(init_net.dev_base_head),
};

EXPORT_SYMBOL(init_net);


#define INITIAL_NET_GEN_PTRS	13 
/* +1 for len +2 for rcu_head */


static unsigned int max_gen_ptrs = INITIAL_NET_GEN_PTRS;


static struct net_generic *net_alloc_generic(void) { struct net_generic *ng; size_t generic_size = offsetof(struct net_generic, ptr[max_gen_ptrs]); ng = kzalloc(generic_size, GFP_KERNEL); if (ng) ng->len = max_gen_ptrs; return ng; }

Contributors

PersonTokensPropCommitsCommitProp
eric dumazeteric dumazet51100.00%1100.00%
Total51100.00%1100.00%


static int net_assign_generic(struct net *net, int id, void *data) { struct net_generic *ng, *old_ng; BUG_ON(!mutex_is_locked(&net_mutex)); BUG_ON(id == 0); old_ng = rcu_dereference_protected(net->gen, lockdep_is_held(&net_mutex)); ng = old_ng; if (old_ng->len >= id) goto assign; ng = net_alloc_generic(); if (ng == NULL) return -ENOMEM; /* * Some synchronisation notes: * * The net_generic explores the net->gen array inside rcu * read section. Besides once set the net->gen->ptr[x] * pointer never changes (see rules in netns/generic.h). * * That said, we simply duplicate this array and schedule * the old copy for kfree after a grace period. */ memcpy(&ng->ptr, &old_ng->ptr, old_ng->len * sizeof(void*)); rcu_assign_pointer(net->gen, ng); kfree_rcu(old_ng, rcu); assign: ng->ptr[id - 1] = data; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
jiri pirkojiri pirko12788.19%125.00%
eric dumazeteric dumazet1510.42%250.00%
lai jiangshanlai jiangshan21.39%125.00%
Total144100.00%4100.00%


static int ops_init(const struct pernet_operations *ops, struct net *net) { int err = -ENOMEM; void *data = NULL; if (ops->id && ops->size) { data = kzalloc(ops->size, GFP_KERNEL); if (!data) goto out; err = net_assign_generic(net, *ops->id, data); if (err) goto cleanup; } err = 0; if (ops->init) err = ops->init(net); if (!err) return 0; cleanup: kfree(data); out: return err; }

Contributors

PersonTokensPropCommitsCommitProp
eric w. biedermaneric w. biederman7965.83%150.00%
julian anastasovjulian anastasov4134.17%150.00%
Total120100.00%2100.00%


static void ops_free(const struct pernet_operations *ops, struct net *net) { if (ops->id && ops->size) { int id = *ops->id; kfree(net_generic(net, id)); } }

Contributors

PersonTokensPropCommitsCommitProp
eric w. biedermaneric w. biederman47100.00%1100.00%
Total47100.00%1100.00%


static void ops_exit_list(const struct pernet_operations *ops, struct list_head *net_exit_list) { struct net *net; if (ops->exit) { list_for_each_entry(net, net_exit_list, exit_list) ops->exit(net); } if (ops->exit_batch) ops->exit_batch(net_exit_list); }

Contributors

PersonTokensPropCommitsCommitProp
eric w. biedermaneric w. biederman58100.00%1100.00%
Total58100.00%1100.00%


static void ops_free_list(const struct pernet_operations *ops, struct list_head *net_exit_list) { struct net *net; if (ops->size && ops->id) { list_for_each_entry(net, net_exit_list, exit_list) ops_free(ops, net); } }

Contributors

PersonTokensPropCommitsCommitProp
eric w. biedermaneric w. biederman49100.00%1100.00%
Total49100.00%1100.00%

/* should be called with nsid_lock held */
static int alloc_netid(struct net *net, struct net *peer, int reqid) { int min = 0, max = 0; if (reqid >= 0) { min = reqid; max = reqid + 1; } return idr_alloc(&net->netns_ids, peer, min, max, GFP_ATOMIC); }

Contributors

PersonTokensPropCommitsCommitProp
nicolas dichtelnicolas dichtel63100.00%3100.00%
Total63100.00%3100.00%

/* This function is used by idr_for_each(). If net is equal to peer, the * function returns the id so that idr_for_each() stops. Because we cannot * returns the id 0 (idr_for_each() will not stop), we return the magic value * NET_ID_ZERO (-1) for it. */ #define NET_ID_ZERO -1
static int net_eq_idr(int id, void *net, void *peer) { if (net_eq(net, peer)) return id ? : NET_ID_ZERO; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
nicolas dichtelnicolas dichtel35100.00%1100.00%
Total35100.00%1100.00%

/* Should be called with nsid_lock held. If a new id is assigned, the bool alloc * is set to true, thus the caller knows that the new id must be notified via * rtnl. */
static int __peernet2id_alloc(struct net *net, struct net *peer, bool *alloc) { int id = idr_for_each(&net->netns_ids, net_eq_idr, peer); bool alloc_it = *alloc; *alloc = false; /* Magic value for id 0. */ if (id == NET_ID_ZERO) return 0; if (id > 0) return id; if (alloc_it) { id = alloc_netid(net, peer, -1); *alloc = true; return id >= 0 ? id : NETNSA_NSID_NOT_ASSIGNED; } return NETNSA_NSID_NOT_ASSIGNED; }

Contributors

PersonTokensPropCommitsCommitProp
nicolas dichtelnicolas dichtel100100.00%3100.00%
Total100100.00%3100.00%

/* should be called with nsid_lock held */
static int __peernet2id(struct net *net, struct net *peer) { bool no = false; return __peernet2id_alloc(net, peer, &no); }

Contributors

PersonTokensPropCommitsCommitProp
nicolas dichtelnicolas dichtel32100.00%1100.00%
Total32100.00%1100.00%

static void rtnl_net_notifyid(struct net *net, int cmd, int id); /* This function returns the id of a peer netns. If no id is assigned, one will * be allocated and returned. */
int peernet2id_alloc(struct net *net, struct net *peer) { unsigned long flags; bool alloc; int id; spin_lock_irqsave(&net->nsid_lock, flags); alloc = atomic_read(&peer->count) == 0 ? false : true; id = __peernet2id_alloc(net, peer, &alloc); spin_unlock_irqrestore(&net->nsid_lock, flags); if (alloc && id >= 0) rtnl_net_notifyid(net, RTM_NEWNSID, id); return id; }

Contributors

PersonTokensPropCommitsCommitProp
nicolas dichtelnicolas dichtel8995.70%583.33%
americo wangamerico wang44.30%116.67%
Total93100.00%6100.00%

EXPORT_SYMBOL(peernet2id_alloc); /* This function returns, if assigned, the id of a peer netns. */
int peernet2id(struct net *net, struct net *peer) { unsigned long flags; int id; spin_lock_irqsave(&net->nsid_lock, flags); id = __peernet2id(net, peer); spin_unlock_irqrestore(&net->nsid_lock, flags); return id; }

Contributors

PersonTokensPropCommitsCommitProp
nicolas dichtelnicolas dichtel5092.59%150.00%
americo wangamerico wang47.41%150.00%
Total54100.00%2100.00%

/* This function returns true is the peer netns has an id assigned into the * current netns. */
bool peernet_has_id(struct net *net, struct net *peer) { return peernet2id(net, peer) >= 0; }

Contributors

PersonTokensPropCommitsCommitProp
nicolas dichtelnicolas dichtel25100.00%1100.00%
Total25100.00%1100.00%


struct net *get_net_ns_by_id(struct net *net, int id) { unsigned long flags; struct net *peer; if (id < 0) return NULL; rcu_read_lock(); spin_lock_irqsave(&net->nsid_lock, flags); peer = idr_find(&net->netns_ids, id); if (peer) get_net(peer); spin_unlock_irqrestore(&net->nsid_lock, flags); rcu_read_unlock(); return peer; }

Contributors

PersonTokensPropCommitsCommitProp
nicolas dichtelnicolas dichtel7995.18%266.67%
americo wangamerico wang44.82%133.33%
Total83100.00%3100.00%

/* * setup_net runs the initializers for the network namespace object. */
static __net_init int setup_net(struct net *net, struct user_namespace *user_ns) { /* Must be called with net_mutex held */ const struct pernet_operations *ops, *saved_ops; int error = 0; LIST_HEAD(net_exit_list); atomic_set(&net->count, 1); atomic_set(&net->passive, 1); net->dev_base_seq = 1; net->user_ns = user_ns; idr_init(&net->netns_ids); spin_lock_init(&net->nsid_lock); list_for_each_entry(ops, &pernet_list, list) { error = ops_init(ops, net); if (error < 0) goto out_undo; } out: return error; out_undo: /* Walk through the list backwards calling the exit functions * for the pernet modules whose init functions did not fail. */ list_add(&net->exit_list, &net_exit_list); saved_ops = ops; list_for_each_entry_continue_reverse(ops, &pernet_list, list) ops_exit_list(ops, &net_exit_list); ops = saved_ops; list_for_each_entry_continue_reverse(ops, &pernet_list, list) ops_free_list(ops, &net_exit_list); rcu_barrier(); goto out; }

Contributors

PersonTokensPropCommitsCommitProp
eric w. biedermaneric w. biederman13173.60%535.71%
al viroal viro105.62%17.14%
pavel emelianovpavel emelianov105.62%321.43%
nicolas dichtelnicolas dichtel84.49%17.14%
americo wangamerico wang84.49%17.14%
thomas grafthomas graf63.37%17.14%
daniel lezcanodaniel lezcano52.81%214.29%
Total178100.00%14100.00%

#ifdef CONFIG_NET_NS static struct kmem_cache *net_cachep; static struct workqueue_struct *netns_wq;
static struct net *net_alloc(void) { struct net *net = NULL; struct net_generic *ng; ng = net_alloc_generic(); if (!ng) goto out; net = kmem_cache_zalloc(net_cachep, GFP_KERNEL); if (!net) goto out_free; rcu_assign_pointer(net->gen, ng); out: return net; out_free: kfree(ng); goto out; }

Contributors

PersonTokensPropCommitsCommitProp
daniel lezcanodaniel lezcano5977.63%150.00%
pavel emelianovpavel emelianov1722.37%150.00%
Total76100.00%2100.00%


static void net_free(struct net *net) { kfree(rcu_access_pointer(net->gen)); kmem_cache_free(net_cachep, net); }

Contributors

PersonTokensPropCommitsCommitProp
johann felix sodenjohann felix soden1864.29%133.33%
alexey dobriyanalexey dobriyan725.00%133.33%
eric dumazeteric dumazet310.71%133.33%
Total28100.00%3100.00%


void net_drop_ns(void *p) { struct net *ns = p; if (ns && atomic_dec_and_test(&ns->passive)) net_free(ns); }

Contributors

PersonTokensPropCommitsCommitProp
al viroal viro33100.00%1100.00%
Total33100.00%1100.00%


struct net *copy_net_ns(unsigned long flags, struct user_namespace *user_ns, struct net *old_net) { struct net *net; int rv; if (!(flags & CLONE_NEWNET)) return get_net(old_net); net = net_alloc(); if (!net) return ERR_PTR(-ENOMEM); get_user_ns(user_ns); mutex_lock(&net_mutex); rv = setup_net(net, user_ns); if (rv == 0) { rtnl_lock(); list_add_tail_rcu(&net->list, &net_namespace_list); rtnl_unlock(); } mutex_unlock(&net_mutex); if (rv < 0) { put_user_ns(user_ns); net_drop_ns(net); return ERR_PTR(rv); } return net; }

Contributors

PersonTokensPropCommitsCommitProp
eric w. biedermaneric w. biederman7856.12%337.50%
alexey dobriyanalexey dobriyan3021.58%112.50%
rob landleyrob landley2417.27%112.50%
daniel lezcanodaniel lezcano53.60%112.50%
johannes bergjohannes berg10.72%112.50%
al viroal viro10.72%112.50%
Total139100.00%8100.00%

static DEFINE_SPINLOCK(cleanup_list_lock); static LIST_HEAD(cleanup_list); /* Must hold cleanup_list_lock to touch */
static void cleanup_net(struct work_struct *work) { const struct pernet_operations *ops; struct net *net, *tmp; struct list_head net_kill_list; LIST_HEAD(net_exit_list); /* Atomically snapshot the list of namespaces to cleanup */ spin_lock_irq(&cleanup_list_lock); list_replace_init(&cleanup_list, &net_kill_list); spin_unlock_irq(&cleanup_list_lock); mutex_lock(&net_mutex); /* Don't let anyone else find us. */ rtnl_lock(); list_for_each_entry(net, &net_kill_list, cleanup_list) { list_del_rcu(&net->list); list_add_tail(&net->exit_list, &net_exit_list); for_each_net(tmp) { int id; spin_lock_irq(&tmp->nsid_lock); id = __peernet2id(tmp, net); if (id >= 0) idr_remove(&tmp->netns_ids, id); spin_unlock_irq(&tmp->nsid_lock); if (id >= 0) rtnl_net_notifyid(tmp, RTM_DELNSID, id); } spin_lock_irq(&net->nsid_lock); idr_destroy(&net->netns_ids); spin_unlock_irq(&net->nsid_lock); } rtnl_unlock(); /* * Another CPU might be rcu-iterating the list, wait for it. * This needs to be before calling the exit() notifiers, so * the rcu_barrier() below isn't sufficient alone. */ synchronize_rcu(); /* Run all of the network namespace exit methods */ list_for_each_entry_reverse(ops, &pernet_list, list) ops_exit_list(ops, &net_exit_list); /* Free the net generic variables */ list_for_each_entry_reverse(ops, &pernet_list, list) ops_free_list(ops, &net_exit_list); mutex_unlock(&net_mutex); /* Ensure there are no outstanding rcu callbacks using this * network namespace. */ rcu_barrier(); /* Finally it is safe to free my network namespace structure */ list_for_each_entry_safe(net, tmp, &net_exit_list, exit_list) { list_del_init(&net->exit_list); put_user_ns(net->user_ns); net_drop_ns(net); } }

Contributors

PersonTokensPropCommitsCommitProp
eric w. biedermaneric w. biederman10137.69%433.33%
nicolas dichtelnicolas dichtel8130.22%325.00%
pavel emelianovpavel emelianov6925.75%18.33%
americo wangamerico wang82.99%18.33%
johannes bergjohannes berg51.87%18.33%
xiao jinxiao jin31.12%18.33%
al viroal viro10.37%18.33%
Total268100.00%12100.00%

static DECLARE_WORK(net_cleanup_work, cleanup_net);
void __put_net(struct net *net) { /* Cleanup the network namespace in process context */ unsigned long flags; spin_lock_irqsave(&cleanup_list_lock, flags); list_add(&net->cleanup_list, &cleanup_list); spin_unlock_irqrestore(&cleanup_list_lock, flags); queue_work(netns_wq, &net_cleanup_work); }

Contributors

PersonTokensPropCommitsCommitProp
eric w. biedermaneric w. biederman2550.00%133.33%
pavel emelianovpavel emelianov2244.00%133.33%
benjamin therybenjamin thery36.00%133.33%
Total50100.00%3100.00%

EXPORT_SYMBOL_GPL(__put_net);
struct net *get_net_ns_by_fd(int fd) { struct file *file; struct ns_common *ns; struct net *net; file = proc_ns_fget(fd); if (IS_ERR(file)) return ERR_CAST(file); ns = get_proc_ns(file_inode(file)); if (ns->ops == &netns_operations) net = get_net(container_of(ns, struct net, ns)); else net = ERR_PTR(-EINVAL); fput(file); return net; }

Contributors

PersonTokensPropCommitsCommitProp
stephen rothwellstephen rothwell5759.38%114.29%
al viroal viro3839.58%571.43%
david howellsdavid howells11.04%114.29%
Total96100.00%7100.00%

#else
struct net *get_net_ns_by_fd(int fd) { return ERR_PTR(-EINVAL); }

Contributors

PersonTokensPropCommitsCommitProp
stephen rothwellstephen rothwell17100.00%1100.00%
Total17100.00%1100.00%

#endif EXPORT_SYMBOL_GPL(get_net_ns_by_fd);
struct net *get_net_ns_by_pid(pid_t pid) { struct task_struct *tsk; struct net *net; /* Lookup the network namespace */ net = ERR_PTR(-ESRCH); rcu_read_lock(); tsk = find_task_by_vpid(pid); if (tsk) { struct nsproxy *nsproxy; task_lock(tsk); nsproxy = tsk->nsproxy; if (nsproxy) net = get_net(nsproxy->net_ns); task_unlock(tsk); } rcu_read_unlock(); return net; }

Contributors

PersonTokensPropCommitsCommitProp
johannes bergjohannes berg7385.88%150.00%
eric w. biedermaneric w. biederman1214.12%150.00%
Total85100.00%2100.00%

EXPORT_SYMBOL_GPL(get_net_ns_by_pid);
static __net_init int net_ns_net_init(struct net *net) { #ifdef CONFIG_NET_NS net->ns.ops = &netns_operations; #endif return ns_alloc_inum(&net->ns); }

Contributors

PersonTokensPropCommitsCommitProp
eric w. biedermaneric w. biederman1954.29%125.00%
al viroal viro1645.71%375.00%
Total35100.00%4100.00%


static __net_exit void net_ns_net_exit(struct net *net) { ns_free_inum(&net->ns); }

Contributors

PersonTokensPropCommitsCommitProp
eric w. biedermaneric w. biederman1785.00%133.33%
al viroal viro315.00%266.67%
Total20100.00%3100.00%

static struct pernet_operations __net_initdata net_ns_ops = { .init = net_ns_net_init, .exit = net_ns_net_exit, }; static struct nla_policy rtnl_net_policy[NETNSA_MAX + 1] = { [NETNSA_NONE] = { .type = NLA_UNSPEC }, [NETNSA_NSID] = { .type = NLA_S32 }, [NETNSA_PID] = { .type = NLA_U32 }, [NETNSA_FD] = { .type = NLA_U32 }, };
static int rtnl_net_newid(struct sk_buff *skb, struct nlmsghdr *nlh) { struct net *net = sock_net(skb->sk); struct nlattr *tb[NETNSA_MAX + 1]; unsigned long flags; struct net *peer; int nsid, err; err = nlmsg_parse(nlh, sizeof(struct rtgenmsg), tb, NETNSA_MAX, rtnl_net_policy); if (err < 0) return err; if (!tb[NETNSA_NSID]) return -EINVAL; nsid = nla_get_s32(tb[NETNSA_NSID]); if (tb[NETNSA_PID]) peer = get_net_ns_by_pid(nla_get_u32(tb[NETNSA_PID])); else if (tb[NETNSA_FD]) peer = get_net_ns_by_fd(nla_get_u32(tb[NETNSA_FD])); else return -EINVAL; if (IS_ERR(peer)) return PTR_ERR(peer); spin_lock_irqsave(&net->nsid_lock, flags); if (__peernet2id(net, peer) >= 0) { spin_unlock_irqrestore(&net->nsid_lock, flags); err = -EEXIST; goto out; } err = alloc_netid(net, peer, nsid); spin_unlock_irqrestore(&net->nsid_lock, flags); if (err >= 0) { rtnl_net_notifyid(net, RTM_NEWNSID, err); err = 0; } out: put_net(peer); return err; }

Contributors

PersonTokensPropCommitsCommitProp
nicolas dichtelnicolas dichtel19375.98%440.00%
eric w. biedermaneric w. biederman3112.20%220.00%
daniel lezcanodaniel lezcano114.33%110.00%
benjamin therybenjamin thery103.94%110.00%
americo wangamerico wang62.36%110.00%
stephen hemmingerstephen hemminger31.18%110.00%
Total254100.00%10100.00%


static int rtnl_net_get_size(void) { return NLMSG_ALIGN(sizeof(struct rtgenmsg)) + nla_total_size(sizeof(s32)) /* NETNSA_NSID */ ; }

Contributors

PersonTokensPropCommitsCommitProp
nicolas dichtelnicolas dichtel27100.00%1100.00%
Total27100.00%1100.00%


static int rtnl_net_fill(struct sk_buff *skb, u32 portid, u32 seq, int flags, int cmd, struct net *net, int nsid) { struct nlmsghdr *nlh; struct rtgenmsg *rth; nlh = nlmsg_put(skb, portid, seq, cmd, sizeof(*rth), flags); if (!nlh) return -EMSGSIZE; rth = nlmsg_data(nlh); rth->rtgen_family = AF_UNSPEC; if (nla_put_s32(skb, NETNSA_NSID, nsid)) goto nla_put_failure; nlmsg_end(skb, nlh); return 0; nla_put_failure: nlmsg_cancel(skb, nlh); return -EMSGSIZE; }

Contributors

PersonTokensPropCommitsCommitProp
nicolas dichtelnicolas dichtel121100.00%3100.00%
Total121100.00%3100.00%


static int rtnl_net_getid(struct sk_buff *skb, struct nlmsghdr *nlh) { struct net *net = sock_net(skb->sk); struct nlattr *tb[NETNSA_MAX + 1]; struct sk_buff *msg; struct net *peer; int err, id; err = nlmsg_parse(nlh, sizeof(struct rtgenmsg), tb, NETNSA_MAX, rtnl_net_policy); if (err < 0) return err; if (tb[NETNSA_PID]) peer = get_net_ns_by_pid(nla_get_u32(tb[NETNSA_PID])); else if (tb[NETNSA_FD]) peer = get_net_ns_by_fd(nla_get_u32(tb[NETNSA_FD])); else return -EINVAL; if (IS_ERR(peer)) return PTR_ERR(peer); msg = nlmsg_new(rtnl_net_get_size(), GFP_KERNEL); if (!msg) { err = -ENOMEM; goto out; } id = peernet2id(net, peer); err = rtnl_net_fill(msg, NETLINK_CB(skb).portid, nlh->nlmsg_seq,