xref: /openbmc/linux/fs/proc/proc_net.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
1457c8996SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
23c12afe7SDavid S. Miller /*
33c12afe7SDavid S. Miller  *  linux/fs/proc/net.c
43c12afe7SDavid S. Miller  *
53c12afe7SDavid S. Miller  *  Copyright (C) 2007
63c12afe7SDavid S. Miller  *
73c12afe7SDavid S. Miller  *  Author: Eric Biederman <ebiederm@xmission.com>
83c12afe7SDavid S. Miller  *
93c12afe7SDavid S. Miller  *  proc net directory handling functions
103c12afe7SDavid S. Miller  */
113c12afe7SDavid S. Miller #include <linux/errno.h>
123c12afe7SDavid S. Miller #include <linux/time.h>
133c12afe7SDavid S. Miller #include <linux/proc_fs.h>
143c12afe7SDavid S. Miller #include <linux/stat.h>
155a0e3ad6STejun Heo #include <linux/slab.h>
163c12afe7SDavid S. Miller #include <linux/init.h>
173c12afe7SDavid S. Miller #include <linux/sched.h>
18f719ff9bSIngo Molnar #include <linux/sched/task.h>
193c12afe7SDavid S. Miller #include <linux/module.h>
203c12afe7SDavid S. Miller #include <linux/bitops.h>
213c12afe7SDavid S. Miller #include <linux/mount.h>
223c12afe7SDavid S. Miller #include <linux/nsproxy.h>
23c110486fSDmitry Torokhov #include <linux/uidgid.h>
243c12afe7SDavid S. Miller #include <net/net_namespace.h>
25e372c414SDenis V. Lunev #include <linux/seq_file.h>
263c12afe7SDavid S. Miller 
273c12afe7SDavid S. Miller #include "internal.h"
283c12afe7SDavid S. Miller 
PDE_NET(struct proc_dir_entry * pde)294abfd029SDavid Howells static inline struct net *PDE_NET(struct proc_dir_entry *pde)
304abfd029SDavid Howells {
314abfd029SDavid Howells 	return pde->parent->data;
324abfd029SDavid Howells }
333c12afe7SDavid S. Miller 
get_proc_net(const struct inode * inode)348086cd45SAdrian Bunk static struct net *get_proc_net(const struct inode *inode)
358086cd45SAdrian Bunk {
368086cd45SAdrian Bunk 	return maybe_get_net(PDE_NET(PDE(inode)));
378086cd45SAdrian Bunk }
388086cd45SAdrian Bunk 
seq_open_net(struct inode * inode,struct file * file)39c3506372SChristoph Hellwig static int seq_open_net(struct inode *inode, struct file *file)
40e372c414SDenis V. Lunev {
41c3506372SChristoph Hellwig 	unsigned int state_size = PDE(inode)->state_size;
42e372c414SDenis V. Lunev 	struct seq_net_private *p;
43c3506372SChristoph Hellwig 	struct net *net;
44e372c414SDenis V. Lunev 
45c3506372SChristoph Hellwig 	WARN_ON_ONCE(state_size < sizeof(*p));
46e372c414SDenis V. Lunev 
47564def71SDavid Howells 	if (file->f_mode & FMODE_WRITE && !PDE(inode)->write)
48564def71SDavid Howells 		return -EACCES;
49564def71SDavid Howells 
50c3506372SChristoph Hellwig 	net = get_proc_net(inode);
51c3506372SChristoph Hellwig 	if (!net)
52e372c414SDenis V. Lunev 		return -ENXIO;
53e372c414SDenis V. Lunev 
54c3506372SChristoph Hellwig 	p = __seq_open_private(file, PDE(inode)->seq_ops, state_size);
55c3506372SChristoph Hellwig 	if (!p) {
56e372c414SDenis V. Lunev 		put_net(net);
57e372c414SDenis V. Lunev 		return -ENOMEM;
58e372c414SDenis V. Lunev 	}
591218854aSYOSHIFUJI Hideaki #ifdef CONFIG_NET_NS
60e372c414SDenis V. Lunev 	p->net = net;
6104a931e5SEric Dumazet 	netns_tracker_alloc(net, &p->ns_tracker, GFP_KERNEL);
621218854aSYOSHIFUJI Hideaki #endif
63e372c414SDenis V. Lunev 	return 0;
64e372c414SDenis V. Lunev }
65c3506372SChristoph Hellwig 
seq_file_net_put_net(struct seq_file * seq)6604a931e5SEric Dumazet static void seq_file_net_put_net(struct seq_file *seq)
6704a931e5SEric Dumazet {
6804a931e5SEric Dumazet #ifdef CONFIG_NET_NS
6904a931e5SEric Dumazet 	struct seq_net_private *priv = seq->private;
7004a931e5SEric Dumazet 
7104a931e5SEric Dumazet 	put_net_track(priv->net, &priv->ns_tracker);
7204a931e5SEric Dumazet #else
7304a931e5SEric Dumazet 	put_net(&init_net);
7404a931e5SEric Dumazet #endif
7504a931e5SEric Dumazet }
7604a931e5SEric Dumazet 
seq_release_net(struct inode * ino,struct file * f)77c3506372SChristoph Hellwig static int seq_release_net(struct inode *ino, struct file *f)
78c3506372SChristoph Hellwig {
79c3506372SChristoph Hellwig 	struct seq_file *seq = f->private_data;
80c3506372SChristoph Hellwig 
8104a931e5SEric Dumazet 	seq_file_net_put_net(seq);
82c3506372SChristoph Hellwig 	seq_release_private(ino, f);
83c3506372SChristoph Hellwig 	return 0;
84c3506372SChristoph Hellwig }
85c3506372SChristoph Hellwig 
86d56c0d45SAlexey Dobriyan static const struct proc_ops proc_net_seq_ops = {
87d56c0d45SAlexey Dobriyan 	.proc_open	= seq_open_net,
88d56c0d45SAlexey Dobriyan 	.proc_read	= seq_read,
89d56c0d45SAlexey Dobriyan 	.proc_write	= proc_simple_write,
90d56c0d45SAlexey Dobriyan 	.proc_lseek	= seq_lseek,
91d56c0d45SAlexey Dobriyan 	.proc_release	= seq_release_net,
92c3506372SChristoph Hellwig };
93c3506372SChristoph Hellwig 
bpf_iter_init_seq_net(void * priv_data,struct bpf_iter_aux_info * aux)94f9c79272SYonghong Song int bpf_iter_init_seq_net(void *priv_data, struct bpf_iter_aux_info *aux)
95138d0be3SYonghong Song {
96138d0be3SYonghong Song #ifdef CONFIG_NET_NS
97138d0be3SYonghong Song 	struct seq_net_private *p = priv_data;
98138d0be3SYonghong Song 
9904a931e5SEric Dumazet 	p->net = get_net_track(current->nsproxy->net_ns, &p->ns_tracker,
10004a931e5SEric Dumazet 			       GFP_KERNEL);
101138d0be3SYonghong Song #endif
102138d0be3SYonghong Song 	return 0;
103138d0be3SYonghong Song }
104138d0be3SYonghong Song 
bpf_iter_fini_seq_net(void * priv_data)105138d0be3SYonghong Song void bpf_iter_fini_seq_net(void *priv_data)
106138d0be3SYonghong Song {
107138d0be3SYonghong Song #ifdef CONFIG_NET_NS
108138d0be3SYonghong Song 	struct seq_net_private *p = priv_data;
109138d0be3SYonghong Song 
11004a931e5SEric Dumazet 	put_net_track(p->net, &p->ns_tracker);
111138d0be3SYonghong Song #endif
112138d0be3SYonghong Song }
113138d0be3SYonghong Song 
proc_create_net_data(const char * name,umode_t mode,struct proc_dir_entry * parent,const struct seq_operations * ops,unsigned int state_size,void * data)114c3506372SChristoph Hellwig struct proc_dir_entry *proc_create_net_data(const char *name, umode_t mode,
115c3506372SChristoph Hellwig 		struct proc_dir_entry *parent, const struct seq_operations *ops,
116c3506372SChristoph Hellwig 		unsigned int state_size, void *data)
117c3506372SChristoph Hellwig {
118c3506372SChristoph Hellwig 	struct proc_dir_entry *p;
119c3506372SChristoph Hellwig 
120c3506372SChristoph Hellwig 	p = proc_create_reg(name, mode, &parent, data);
121c3506372SChristoph Hellwig 	if (!p)
122c3506372SChristoph Hellwig 		return NULL;
1231fde6f21SAlexey Dobriyan 	pde_force_lookup(p);
124d56c0d45SAlexey Dobriyan 	p->proc_ops = &proc_net_seq_ops;
125c3506372SChristoph Hellwig 	p->seq_ops = ops;
126c3506372SChristoph Hellwig 	p->state_size = state_size;
127c3506372SChristoph Hellwig 	return proc_register(parent, p);
128c3506372SChristoph Hellwig }
129c3506372SChristoph Hellwig EXPORT_SYMBOL_GPL(proc_create_net_data);
130e372c414SDenis V. Lunev 
131564def71SDavid Howells /**
132564def71SDavid Howells  * proc_create_net_data_write - Create a writable net_ns-specific proc file
133564def71SDavid Howells  * @name: The name of the file.
134564def71SDavid Howells  * @mode: The file's access mode.
135564def71SDavid Howells  * @parent: The parent directory in which to create.
136564def71SDavid Howells  * @ops: The seq_file ops with which to read the file.
137d2928e85SRandy Dunlap  * @write: The write method with which to 'modify' the file.
138359745d7SMuchun Song  * @data: Data for retrieval by pde_data().
139564def71SDavid Howells  *
140564def71SDavid Howells  * Create a network namespaced proc file in the @parent directory with the
141564def71SDavid Howells  * specified @name and @mode that allows reading of a file that displays a
142564def71SDavid Howells  * series of elements and also provides for the file accepting writes that have
143564def71SDavid Howells  * some arbitrary effect.
144564def71SDavid Howells  *
145564def71SDavid Howells  * The functions in the @ops table are used to iterate over items to be
146564def71SDavid Howells  * presented and extract the readable content using the seq_file interface.
147564def71SDavid Howells  *
148564def71SDavid Howells  * The @write function is called with the data copied into a kernel space
149564def71SDavid Howells  * scratch buffer and has a NUL appended for convenience.  The buffer may be
150564def71SDavid Howells  * modified by the @write function.  @write should return 0 on success.
151564def71SDavid Howells  *
152564def71SDavid Howells  * The @data value is accessible from the @show and @write functions by calling
153359745d7SMuchun Song  * pde_data() on the file inode.  The network namespace must be accessed by
154564def71SDavid Howells  * calling seq_file_net() on the seq_file struct.
155564def71SDavid Howells  */
proc_create_net_data_write(const char * name,umode_t mode,struct proc_dir_entry * parent,const struct seq_operations * ops,proc_write_t write,unsigned int state_size,void * data)156564def71SDavid Howells struct proc_dir_entry *proc_create_net_data_write(const char *name, umode_t mode,
157564def71SDavid Howells 						  struct proc_dir_entry *parent,
158564def71SDavid Howells 						  const struct seq_operations *ops,
159564def71SDavid Howells 						  proc_write_t write,
160564def71SDavid Howells 						  unsigned int state_size, void *data)
161564def71SDavid Howells {
162564def71SDavid Howells 	struct proc_dir_entry *p;
163564def71SDavid Howells 
164564def71SDavid Howells 	p = proc_create_reg(name, mode, &parent, data);
165564def71SDavid Howells 	if (!p)
166564def71SDavid Howells 		return NULL;
1671fde6f21SAlexey Dobriyan 	pde_force_lookup(p);
168d56c0d45SAlexey Dobriyan 	p->proc_ops = &proc_net_seq_ops;
169564def71SDavid Howells 	p->seq_ops = ops;
170564def71SDavid Howells 	p->state_size = state_size;
171564def71SDavid Howells 	p->write = write;
172564def71SDavid Howells 	return proc_register(parent, p);
173564def71SDavid Howells }
174564def71SDavid Howells EXPORT_SYMBOL_GPL(proc_create_net_data_write);
175564def71SDavid Howells 
single_open_net(struct inode * inode,struct file * file)1763617d949SChristoph Hellwig static int single_open_net(struct inode *inode, struct file *file)
177de05c557SPavel Emelyanov {
1783617d949SChristoph Hellwig 	struct proc_dir_entry *de = PDE(inode);
179de05c557SPavel Emelyanov 	struct net *net;
1803617d949SChristoph Hellwig 	int err;
181de05c557SPavel Emelyanov 
182de05c557SPavel Emelyanov 	net = get_proc_net(inode);
1833617d949SChristoph Hellwig 	if (!net)
1843617d949SChristoph Hellwig 		return -ENXIO;
185de05c557SPavel Emelyanov 
1863617d949SChristoph Hellwig 	err = single_open(file, de->single_show, net);
1873617d949SChristoph Hellwig 	if (err)
188de05c557SPavel Emelyanov 		put_net(net);
189de05c557SPavel Emelyanov 	return err;
190de05c557SPavel Emelyanov }
191de05c557SPavel Emelyanov 
single_release_net(struct inode * ino,struct file * f)1923617d949SChristoph Hellwig static int single_release_net(struct inode *ino, struct file *f)
193b6fcbdb4SPavel Emelyanov {
194b6fcbdb4SPavel Emelyanov 	struct seq_file *seq = f->private_data;
195b6fcbdb4SPavel Emelyanov 	put_net(seq->private);
196b6fcbdb4SPavel Emelyanov 	return single_release(ino, f);
197b6fcbdb4SPavel Emelyanov }
1983617d949SChristoph Hellwig 
199d56c0d45SAlexey Dobriyan static const struct proc_ops proc_net_single_ops = {
200d56c0d45SAlexey Dobriyan 	.proc_open	= single_open_net,
201d56c0d45SAlexey Dobriyan 	.proc_read	= seq_read,
202d56c0d45SAlexey Dobriyan 	.proc_write	= proc_simple_write,
203d56c0d45SAlexey Dobriyan 	.proc_lseek	= seq_lseek,
204d56c0d45SAlexey Dobriyan 	.proc_release	= single_release_net,
2053617d949SChristoph Hellwig };
2063617d949SChristoph Hellwig 
proc_create_net_single(const char * name,umode_t mode,struct proc_dir_entry * parent,int (* show)(struct seq_file *,void *),void * data)2073617d949SChristoph Hellwig struct proc_dir_entry *proc_create_net_single(const char *name, umode_t mode,
2083617d949SChristoph Hellwig 		struct proc_dir_entry *parent,
2093617d949SChristoph Hellwig 		int (*show)(struct seq_file *, void *), void *data)
2103617d949SChristoph Hellwig {
2113617d949SChristoph Hellwig 	struct proc_dir_entry *p;
2123617d949SChristoph Hellwig 
2133617d949SChristoph Hellwig 	p = proc_create_reg(name, mode, &parent, data);
2143617d949SChristoph Hellwig 	if (!p)
2153617d949SChristoph Hellwig 		return NULL;
2161fde6f21SAlexey Dobriyan 	pde_force_lookup(p);
217d56c0d45SAlexey Dobriyan 	p->proc_ops = &proc_net_single_ops;
2183617d949SChristoph Hellwig 	p->single_show = show;
2193617d949SChristoph Hellwig 	return proc_register(parent, p);
2203617d949SChristoph Hellwig }
2213617d949SChristoph Hellwig EXPORT_SYMBOL_GPL(proc_create_net_single);
222b6fcbdb4SPavel Emelyanov 
223564def71SDavid Howells /**
224564def71SDavid Howells  * proc_create_net_single_write - Create a writable net_ns-specific proc file
225564def71SDavid Howells  * @name: The name of the file.
226564def71SDavid Howells  * @mode: The file's access mode.
227564def71SDavid Howells  * @parent: The parent directory in which to create.
228564def71SDavid Howells  * @show: The seqfile show method with which to read the file.
229d2928e85SRandy Dunlap  * @write: The write method with which to 'modify' the file.
230359745d7SMuchun Song  * @data: Data for retrieval by pde_data().
231564def71SDavid Howells  *
232564def71SDavid Howells  * Create a network-namespaced proc file in the @parent directory with the
233564def71SDavid Howells  * specified @name and @mode that allows reading of a file that displays a
234564def71SDavid Howells  * single element rather than a series and also provides for the file accepting
235564def71SDavid Howells  * writes that have some arbitrary effect.
236564def71SDavid Howells  *
237564def71SDavid Howells  * The @show function is called to extract the readable content via the
238564def71SDavid Howells  * seq_file interface.
239564def71SDavid Howells  *
240564def71SDavid Howells  * The @write function is called with the data copied into a kernel space
241564def71SDavid Howells  * scratch buffer and has a NUL appended for convenience.  The buffer may be
242564def71SDavid Howells  * modified by the @write function.  @write should return 0 on success.
243564def71SDavid Howells  *
244564def71SDavid Howells  * The @data value is accessible from the @show and @write functions by calling
245359745d7SMuchun Song  * pde_data() on the file inode.  The network namespace must be accessed by
246564def71SDavid Howells  * calling seq_file_single_net() on the seq_file struct.
247564def71SDavid Howells  */
proc_create_net_single_write(const char * name,umode_t mode,struct proc_dir_entry * parent,int (* show)(struct seq_file *,void *),proc_write_t write,void * data)248564def71SDavid Howells struct proc_dir_entry *proc_create_net_single_write(const char *name, umode_t mode,
249564def71SDavid Howells 						    struct proc_dir_entry *parent,
250564def71SDavid Howells 						    int (*show)(struct seq_file *, void *),
251564def71SDavid Howells 						    proc_write_t write,
252564def71SDavid Howells 						    void *data)
253564def71SDavid Howells {
254564def71SDavid Howells 	struct proc_dir_entry *p;
255564def71SDavid Howells 
256564def71SDavid Howells 	p = proc_create_reg(name, mode, &parent, data);
257564def71SDavid Howells 	if (!p)
258564def71SDavid Howells 		return NULL;
2591fde6f21SAlexey Dobriyan 	pde_force_lookup(p);
260d56c0d45SAlexey Dobriyan 	p->proc_ops = &proc_net_single_ops;
261564def71SDavid Howells 	p->single_show = show;
262564def71SDavid Howells 	p->write = write;
263564def71SDavid Howells 	return proc_register(parent, p);
264564def71SDavid Howells }
265564def71SDavid Howells EXPORT_SYMBOL_GPL(proc_create_net_single_write);
266564def71SDavid Howells 
get_proc_task_net(struct inode * dir)267e9720acdSPavel Emelyanov static struct net *get_proc_task_net(struct inode *dir)
268e9720acdSPavel Emelyanov {
269e9720acdSPavel Emelyanov 	struct task_struct *task;
270e9720acdSPavel Emelyanov 	struct nsproxy *ns;
271e9720acdSPavel Emelyanov 	struct net *net = NULL;
272e9720acdSPavel Emelyanov 
273e9720acdSPavel Emelyanov 	rcu_read_lock();
274e9720acdSPavel Emelyanov 	task = pid_task(proc_pid(dir), PIDTYPE_PID);
275e9720acdSPavel Emelyanov 	if (task != NULL) {
276728dba3aSEric W. Biederman 		task_lock(task);
277728dba3aSEric W. Biederman 		ns = task->nsproxy;
278e9720acdSPavel Emelyanov 		if (ns != NULL)
279e9720acdSPavel Emelyanov 			net = get_net(ns->net_ns);
280728dba3aSEric W. Biederman 		task_unlock(task);
281e9720acdSPavel Emelyanov 	}
282e9720acdSPavel Emelyanov 	rcu_read_unlock();
283e9720acdSPavel Emelyanov 
284e9720acdSPavel Emelyanov 	return net;
285e9720acdSPavel Emelyanov }
286e9720acdSPavel Emelyanov 
proc_tgid_net_lookup(struct inode * dir,struct dentry * dentry,unsigned int flags)287e9720acdSPavel Emelyanov static struct dentry *proc_tgid_net_lookup(struct inode *dir,
28800cd8dd3SAl Viro 		struct dentry *dentry, unsigned int flags)
289e9720acdSPavel Emelyanov {
290e9720acdSPavel Emelyanov 	struct dentry *de;
291e9720acdSPavel Emelyanov 	struct net *net;
292e9720acdSPavel Emelyanov 
293e9720acdSPavel Emelyanov 	de = ERR_PTR(-ENOENT);
294e9720acdSPavel Emelyanov 	net = get_proc_task_net(dir);
295e9720acdSPavel Emelyanov 	if (net != NULL) {
29693ad5bc6SAlexey Dobriyan 		de = proc_lookup_de(dir, dentry, net->proc_net);
297e9720acdSPavel Emelyanov 		put_net(net);
298e9720acdSPavel Emelyanov 	}
299e9720acdSPavel Emelyanov 	return de;
300e9720acdSPavel Emelyanov }
301e9720acdSPavel Emelyanov 
proc_tgid_net_getattr(struct mnt_idmap * idmap,const struct path * path,struct kstat * stat,u32 request_mask,unsigned int query_flags)302b74d24f7SChristian Brauner static int proc_tgid_net_getattr(struct mnt_idmap *idmap,
303549c7297SChristian Brauner 				 const struct path *path, struct kstat *stat,
304a528d35eSDavid Howells 				 u32 request_mask, unsigned int query_flags)
305e9720acdSPavel Emelyanov {
306a528d35eSDavid Howells 	struct inode *inode = d_inode(path->dentry);
307e9720acdSPavel Emelyanov 	struct net *net;
308e9720acdSPavel Emelyanov 
309e9720acdSPavel Emelyanov 	net = get_proc_task_net(inode);
310e9720acdSPavel Emelyanov 
311*0d72b928SJeff Layton 	generic_fillattr(&nop_mnt_idmap, request_mask, inode, stat);
312e9720acdSPavel Emelyanov 
313e9720acdSPavel Emelyanov 	if (net != NULL) {
314e9720acdSPavel Emelyanov 		stat->nlink = net->proc_net->nlink;
315e9720acdSPavel Emelyanov 		put_net(net);
316e9720acdSPavel Emelyanov 	}
317e9720acdSPavel Emelyanov 
318e9720acdSPavel Emelyanov 	return 0;
319e9720acdSPavel Emelyanov }
320e9720acdSPavel Emelyanov 
321e9720acdSPavel Emelyanov const struct inode_operations proc_net_inode_operations = {
322e9720acdSPavel Emelyanov 	.lookup		= proc_tgid_net_lookup,
323e9720acdSPavel Emelyanov 	.getattr	= proc_tgid_net_getattr,
32418e66ae6SThomas Weißschuh 	.setattr        = proc_setattr,
325e9720acdSPavel Emelyanov };
326e9720acdSPavel Emelyanov 
proc_tgid_net_readdir(struct file * file,struct dir_context * ctx)327f0c3b509SAl Viro static int proc_tgid_net_readdir(struct file *file, struct dir_context *ctx)
328e9720acdSPavel Emelyanov {
329e9720acdSPavel Emelyanov 	int ret;
330e9720acdSPavel Emelyanov 	struct net *net;
331e9720acdSPavel Emelyanov 
332e9720acdSPavel Emelyanov 	ret = -EINVAL;
333f0c3b509SAl Viro 	net = get_proc_task_net(file_inode(file));
334e9720acdSPavel Emelyanov 	if (net != NULL) {
33593ad5bc6SAlexey Dobriyan 		ret = proc_readdir_de(file, ctx, net->proc_net);
336e9720acdSPavel Emelyanov 		put_net(net);
337e9720acdSPavel Emelyanov 	}
338e9720acdSPavel Emelyanov 	return ret;
339e9720acdSPavel Emelyanov }
340e9720acdSPavel Emelyanov 
341e9720acdSPavel Emelyanov const struct file_operations proc_net_operations = {
342b4df2b92SAlexey Dobriyan 	.llseek		= generic_file_llseek,
343e9720acdSPavel Emelyanov 	.read		= generic_read_dir,
344f50752eaSAl Viro 	.iterate_shared	= proc_tgid_net_readdir,
345e9720acdSPavel Emelyanov };
346e9720acdSPavel Emelyanov 
proc_net_ns_init(struct net * net)3474665079cSPavel Emelyanov static __net_init int proc_net_ns_init(struct net *net)
3483c12afe7SDavid S. Miller {
349e9720acdSPavel Emelyanov 	struct proc_dir_entry *netd, *net_statd;
350c110486fSDmitry Torokhov 	kuid_t uid;
351c110486fSDmitry Torokhov 	kgid_t gid;
3523c12afe7SDavid S. Miller 	int err;
3533c12afe7SDavid S. Miller 
354ed8fb78dSAlexey Dobriyan 	/*
355ed8fb78dSAlexey Dobriyan 	 * This PDE acts only as an anchor for /proc/${pid}/net hierarchy.
356ed8fb78dSAlexey Dobriyan 	 * Corresponding inode (PDE(inode) == net->proc_net) is never
357ed8fb78dSAlexey Dobriyan 	 * instantiated therefore blanket zeroing is fine.
358ed8fb78dSAlexey Dobriyan 	 * net->proc_net_stat inode is instantiated normally.
359ed8fb78dSAlexey Dobriyan 	 */
3603c12afe7SDavid S. Miller 	err = -ENOMEM;
361b4884f23SAlexey Dobriyan 	netd = kmem_cache_zalloc(proc_dir_entry_cache, GFP_KERNEL);
362e9720acdSPavel Emelyanov 	if (!netd)
3633c12afe7SDavid S. Miller 		goto out;
3643c12afe7SDavid S. Miller 
3654f113437SAlexey Dobriyan 	netd->subdir = RB_ROOT;
366e9720acdSPavel Emelyanov 	netd->data = net;
367e9720acdSPavel Emelyanov 	netd->nlink = 2;
368e9720acdSPavel Emelyanov 	netd->namelen = 3;
369e9720acdSPavel Emelyanov 	netd->parent = &proc_root;
370b4884f23SAlexey Dobriyan 	netd->name = netd->inline_name;
37109570f91SDavid Howells 	memcpy(netd->name, "net", 4);
3723c12afe7SDavid S. Miller 
373c110486fSDmitry Torokhov 	uid = make_kuid(net->user_ns, 0);
374c110486fSDmitry Torokhov 	if (!uid_valid(uid))
375c110486fSDmitry Torokhov 		uid = netd->uid;
376c110486fSDmitry Torokhov 
377c110486fSDmitry Torokhov 	gid = make_kgid(net->user_ns, 0);
378c110486fSDmitry Torokhov 	if (!gid_valid(gid))
379c110486fSDmitry Torokhov 		gid = netd->gid;
380c110486fSDmitry Torokhov 
381c110486fSDmitry Torokhov 	proc_set_user(netd, uid, gid);
382c110486fSDmitry Torokhov 
38370551977SAlexey Dobriyan 	/* Seed dentry revalidation for /proc/${pid}/net */
38470551977SAlexey Dobriyan 	pde_force_lookup(netd);
38570551977SAlexey Dobriyan 
3863c12afe7SDavid S. Miller 	err = -EEXIST;
387e5d69b9fSDenis V. Lunev 	net_statd = proc_net_mkdir(net, "stat", netd);
3883c12afe7SDavid S. Miller 	if (!net_statd)
3893c12afe7SDavid S. Miller 		goto free_net;
3903c12afe7SDavid S. Miller 
3913c12afe7SDavid S. Miller 	net->proc_net = netd;
3923c12afe7SDavid S. Miller 	net->proc_net_stat = net_statd;
393e9720acdSPavel Emelyanov 	return 0;
3943c12afe7SDavid S. Miller 
395e9720acdSPavel Emelyanov free_net:
396b4884f23SAlexey Dobriyan 	pde_free(netd);
3973c12afe7SDavid S. Miller out:
3983c12afe7SDavid S. Miller 	return err;
3993c12afe7SDavid S. Miller }
4003c12afe7SDavid S. Miller 
proc_net_ns_exit(struct net * net)4014665079cSPavel Emelyanov static __net_exit void proc_net_ns_exit(struct net *net)
4023c12afe7SDavid S. Miller {
4033c12afe7SDavid S. Miller 	remove_proc_entry("stat", net->proc_net);
404b4884f23SAlexey Dobriyan 	pde_free(net->proc_net);
4053c12afe7SDavid S. Miller }
4063c12afe7SDavid S. Miller 
407022cbae6SDenis V. Lunev static struct pernet_operations __net_initdata proc_net_ns_ops = {
4083c12afe7SDavid S. Miller 	.init = proc_net_ns_init,
4093c12afe7SDavid S. Miller 	.exit = proc_net_ns_exit,
4103c12afe7SDavid S. Miller };
4113c12afe7SDavid S. Miller 
proc_net_init(void)4124665079cSPavel Emelyanov int __init proc_net_init(void)
4133c12afe7SDavid S. Miller {
414155134feSLinus Torvalds 	proc_symlink("net", NULL, "self/net");
4153c12afe7SDavid S. Miller 
4163c12afe7SDavid S. Miller 	return register_pernet_subsys(&proc_net_ns_ops);
4173c12afe7SDavid S. Miller }
418