proc_net.c 9.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
/*
 *  linux/fs/proc/net.c
 *
 *  Copyright (C) 2007
 *
 *  Author: Eric Biederman <ebiederm@xmission.com>
 *
 *  proc net directory handling functions
 */

11
#include <linux/uaccess.h>
12 13 14 15 16

#include <linux/errno.h>
#include <linux/time.h>
#include <linux/proc_fs.h>
#include <linux/stat.h>
17
#include <linux/slab.h>
18 19
#include <linux/init.h>
#include <linux/sched.h>
20
#include <linux/sched/task.h>
21 22 23 24
#include <linux/module.h>
#include <linux/bitops.h>
#include <linux/mount.h>
#include <linux/nsproxy.h>
25
#include <linux/uidgid.h>
26
#include <net/net_namespace.h>
27
#include <linux/seq_file.h>
28 29 30

#include "internal.h"

31 32 33 34
static inline struct net *PDE_NET(struct proc_dir_entry *pde)
{
	return pde->parent->data;
}
35

A
Adrian Bunk 已提交
36 37 38 39 40
static struct net *get_proc_net(const struct inode *inode)
{
	return maybe_get_net(PDE_NET(PDE(inode)));
}

41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
static int proc_net_d_revalidate(struct dentry *dentry, unsigned int flags)
{
	return 0;
}

static const struct dentry_operations proc_net_dentry_ops = {
	.d_revalidate	= proc_net_d_revalidate,
	.d_delete	= always_delete_dentry,
};

static void pde_force_lookup(struct proc_dir_entry *pde)
{
	/* /proc/net/ entries can be changed under us by setns(CLONE_NEWNET) */
	pde->proc_dops = &proc_net_dentry_ops;
}

57
static int seq_open_net(struct inode *inode, struct file *file)
58
{
59
	unsigned int state_size = PDE(inode)->state_size;
60
	struct seq_net_private *p;
61
	struct net *net;
62

63
	WARN_ON_ONCE(state_size < sizeof(*p));
64

65 66 67
	if (file->f_mode & FMODE_WRITE && !PDE(inode)->write)
		return -EACCES;

68 69
	net = get_proc_net(inode);
	if (!net)
70 71
		return -ENXIO;

72 73
	p = __seq_open_private(file, PDE(inode)->seq_ops, state_size);
	if (!p) {
74 75 76
		put_net(net);
		return -ENOMEM;
	}
77
#ifdef CONFIG_NET_NS
78
	p->net = net;
79
#endif
80 81
	return 0;
}
82 83 84 85 86 87 88 89 90 91 92 93 94

static int seq_release_net(struct inode *ino, struct file *f)
{
	struct seq_file *seq = f->private_data;

	put_net(seq_file_net(seq));
	seq_release_private(ino, f);
	return 0;
}

static const struct file_operations proc_net_seq_fops = {
	.open		= seq_open_net,
	.read		= seq_read,
95
	.write		= proc_simple_write,
96 97 98 99 100 101 102 103 104 105 106 107 108
	.llseek		= seq_lseek,
	.release	= seq_release_net,
};

struct proc_dir_entry *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)
{
	struct proc_dir_entry *p;

	p = proc_create_reg(name, mode, &parent, data);
	if (!p)
		return NULL;
109
	pde_force_lookup(p);
110 111 112 113 114 115
	p->proc_fops = &proc_net_seq_fops;
	p->seq_ops = ops;
	p->state_size = state_size;
	return proc_register(parent, p);
}
EXPORT_SYMBOL_GPL(proc_create_net_data);
116

117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
/**
 * proc_create_net_data_write - Create a writable net_ns-specific proc file
 * @name: The name of the file.
 * @mode: The file's access mode.
 * @parent: The parent directory in which to create.
 * @ops: The seq_file ops with which to read the file.
 * @write: The write method which which to 'modify' the file.
 * @data: Data for retrieval by PDE_DATA().
 *
 * Create a network namespaced proc file in the @parent directory with the
 * specified @name and @mode that allows reading of a file that displays a
 * series of elements and also provides for the file accepting writes that have
 * some arbitrary effect.
 *
 * The functions in the @ops table are used to iterate over items to be
 * presented and extract the readable content using the seq_file interface.
 *
 * The @write function is called with the data copied into a kernel space
 * scratch buffer and has a NUL appended for convenience.  The buffer may be
 * modified by the @write function.  @write should return 0 on success.
 *
 * The @data value is accessible from the @show and @write functions by calling
 * PDE_DATA() on the file inode.  The network namespace must be accessed by
 * calling seq_file_net() on the seq_file struct.
 */
struct proc_dir_entry *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)
{
	struct proc_dir_entry *p;

	p = proc_create_reg(name, mode, &parent, data);
	if (!p)
		return NULL;
153
	pde_force_lookup(p);
154 155 156 157 158 159 160 161
	p->proc_fops = &proc_net_seq_fops;
	p->seq_ops = ops;
	p->state_size = state_size;
	p->write = write;
	return proc_register(parent, p);
}
EXPORT_SYMBOL_GPL(proc_create_net_data_write);

162
static int single_open_net(struct inode *inode, struct file *file)
163
{
164
	struct proc_dir_entry *de = PDE(inode);
165
	struct net *net;
166
	int err;
167 168

	net = get_proc_net(inode);
169 170
	if (!net)
		return -ENXIO;
171

172 173 174
	err = single_open(file, de->single_show, net);
	if (err)
		put_net(net);
175 176 177
	return err;
}

178
static int single_release_net(struct inode *ino, struct file *f)
179 180 181 182 183
{
	struct seq_file *seq = f->private_data;
	put_net(seq->private);
	return single_release(ino, f);
}
184 185 186 187

static const struct file_operations proc_net_single_fops = {
	.open		= single_open_net,
	.read		= seq_read,
188
	.write		= proc_simple_write,
189 190 191 192 193 194 195 196 197 198 199 200 201
	.llseek		= seq_lseek,
	.release	= single_release_net,
};

struct proc_dir_entry *proc_create_net_single(const char *name, umode_t mode,
		struct proc_dir_entry *parent,
		int (*show)(struct seq_file *, void *), void *data)
{
	struct proc_dir_entry *p;

	p = proc_create_reg(name, mode, &parent, data);
	if (!p)
		return NULL;
202
	pde_force_lookup(p);
203 204 205 206 207
	p->proc_fops = &proc_net_single_fops;
	p->single_show = show;
	return proc_register(parent, p);
}
EXPORT_SYMBOL_GPL(proc_create_net_single);
208

209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244
/**
 * proc_create_net_single_write - Create a writable net_ns-specific proc file
 * @name: The name of the file.
 * @mode: The file's access mode.
 * @parent: The parent directory in which to create.
 * @show: The seqfile show method with which to read the file.
 * @write: The write method which which to 'modify' the file.
 * @data: Data for retrieval by PDE_DATA().
 *
 * Create a network-namespaced proc file in the @parent directory with the
 * specified @name and @mode that allows reading of a file that displays a
 * single element rather than a series and also provides for the file accepting
 * writes that have some arbitrary effect.
 *
 * The @show function is called to extract the readable content via the
 * seq_file interface.
 *
 * The @write function is called with the data copied into a kernel space
 * scratch buffer and has a NUL appended for convenience.  The buffer may be
 * modified by the @write function.  @write should return 0 on success.
 *
 * The @data value is accessible from the @show and @write functions by calling
 * PDE_DATA() on the file inode.  The network namespace must be accessed by
 * calling seq_file_single_net() on the seq_file struct.
 */
struct proc_dir_entry *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)
{
	struct proc_dir_entry *p;

	p = proc_create_reg(name, mode, &parent, data);
	if (!p)
		return NULL;
245
	pde_force_lookup(p);
246 247 248 249 250 251 252
	p->proc_fops = &proc_net_single_fops;
	p->single_show = show;
	p->write = write;
	return proc_register(parent, p);
}
EXPORT_SYMBOL_GPL(proc_create_net_single_write);

253 254 255 256 257 258 259 260 261
static struct net *get_proc_task_net(struct inode *dir)
{
	struct task_struct *task;
	struct nsproxy *ns;
	struct net *net = NULL;

	rcu_read_lock();
	task = pid_task(proc_pid(dir), PIDTYPE_PID);
	if (task != NULL) {
262 263
		task_lock(task);
		ns = task->nsproxy;
264 265
		if (ns != NULL)
			net = get_net(ns->net_ns);
266
		task_unlock(task);
267 268 269 270 271 272 273
	}
	rcu_read_unlock();

	return net;
}

static struct dentry *proc_tgid_net_lookup(struct inode *dir,
A
Al Viro 已提交
274
		struct dentry *dentry, unsigned int flags)
275 276 277 278 279 280 281
{
	struct dentry *de;
	struct net *net;

	de = ERR_PTR(-ENOENT);
	net = get_proc_task_net(dir);
	if (net != NULL) {
A
Alexey Dobriyan 已提交
282
		de = proc_lookup_de(dir, dentry, net->proc_net);
283 284 285 286 287
		put_net(net);
	}
	return de;
}

288 289
static int proc_tgid_net_getattr(const struct path *path, struct kstat *stat,
				 u32 request_mask, unsigned int query_flags)
290
{
291
	struct inode *inode = d_inode(path->dentry);
292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310
	struct net *net;

	net = get_proc_task_net(inode);

	generic_fillattr(inode, stat);

	if (net != NULL) {
		stat->nlink = net->proc_net->nlink;
		put_net(net);
	}

	return 0;
}

const struct inode_operations proc_net_inode_operations = {
	.lookup		= proc_tgid_net_lookup,
	.getattr	= proc_tgid_net_getattr,
};

A
Al Viro 已提交
311
static int proc_tgid_net_readdir(struct file *file, struct dir_context *ctx)
312 313 314 315 316
{
	int ret;
	struct net *net;

	ret = -EINVAL;
A
Al Viro 已提交
317
	net = get_proc_task_net(file_inode(file));
318
	if (net != NULL) {
A
Alexey Dobriyan 已提交
319
		ret = proc_readdir_de(file, ctx, net->proc_net);
320 321 322 323 324 325
		put_net(net);
	}
	return ret;
}

const struct file_operations proc_net_operations = {
A
Alexey Dobriyan 已提交
326
	.llseek		= generic_file_llseek,
327
	.read		= generic_read_dir,
328
	.iterate_shared	= proc_tgid_net_readdir,
329 330
};

331
static __net_init int proc_net_ns_init(struct net *net)
332
{
333
	struct proc_dir_entry *netd, *net_statd;
334 335
	kuid_t uid;
	kgid_t gid;
336 337 338
	int err;

	err = -ENOMEM;
339
	netd = kmem_cache_zalloc(proc_dir_entry_cache, GFP_KERNEL);
340
	if (!netd)
341 342
		goto out;

A
Alexey Dobriyan 已提交
343
	netd->subdir = RB_ROOT;
344 345 346 347
	netd->data = net;
	netd->nlink = 2;
	netd->namelen = 3;
	netd->parent = &proc_root;
348
	netd->name = netd->inline_name;
349
	memcpy(netd->name, "net", 4);
350

351 352 353 354 355 356 357 358 359 360
	uid = make_kuid(net->user_ns, 0);
	if (!uid_valid(uid))
		uid = netd->uid;

	gid = make_kgid(net->user_ns, 0);
	if (!gid_valid(gid))
		gid = netd->gid;

	proc_set_user(netd, uid, gid);

361
	err = -EEXIST;
D
Denis V. Lunev 已提交
362
	net_statd = proc_net_mkdir(net, "stat", netd);
363 364 365 366 367
	if (!net_statd)
		goto free_net;

	net->proc_net = netd;
	net->proc_net_stat = net_statd;
368
	return 0;
369

370
free_net:
371
	pde_free(netd);
372 373 374 375
out:
	return err;
}

376
static __net_exit void proc_net_ns_exit(struct net *net)
377 378
{
	remove_proc_entry("stat", net->proc_net);
379
	pde_free(net->proc_net);
380 381
}

382
static struct pernet_operations __net_initdata proc_net_ns_ops = {
383 384 385 386
	.init = proc_net_ns_init,
	.exit = proc_net_ns_exit,
};

387
int __init proc_net_init(void)
388
{
389
	proc_symlink("net", NULL, "self/net");
390 391 392

	return register_pernet_subsys(&proc_net_ns_ops);
}