netns: add /proc/*/net/id symlink
Subsystems (3)
PATCH 1/1 netns: add /proc/*/net/id symlink
David Lamparter pointed some real scenarios where knowing if two processes live in same netns is important, like "how do I kill _all_ processes in netns to shutdown it".
Currently only kernel knows if two netns are the same. Userspace maybe can look at different proc files to find a match indirectly sysconf-style but result will be ugly no matter what.
Add /proc/*/net/id symlink which "points" to an integer.
$ readlink /proc/net/id 0
$ readlink /proc/2941/net/id 1
"id" is not a file because 1 syscall is faster than 3 syscalls.
The only rules and expectations for userspace are:
[as if they will comply, ha-ha]
* init_net always has id 0
* two netns do not have same id
* id is unsigned integer
Kernel code continues to use net_eq(), there is no need
to compare net->id inside kernel, because it is slower than net_eq().
---
fs/proc/generic.c | 16 +++++++++++++ fs/proc/proc_net.c | 31 ++++++++++++++++++++++++- include/linux/proc_fs.h | 7 +++++ include/net/net_namespace.h | 10 ++++++++ net/core/net_namespace.c | 54 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 117 insertions(+), 1 deletion(-)
fs/proc/generic.c +16 -0
@@ -660,6 +660,22 @@ struct proc_dir_entry *proc_symlink(const char *name, }EXPORT_SYMBOL (proc_symlink); +struct proc_dir_entry *_proc_symlink (const char *name,struct proc_dir_entry *parent,const struct inode_operations *proc_iops) +{ +struct proc_dir_entry *pde; + + pde =__proc_create (&parent, name,S_IFLNK |S_IRUGO |S_IWUGO |S_IXUGO ,1 ); +if (!pde) +return NULL ; + pde->proc_iops = proc_iops; + pde->data =NULL ; +if (proc_register (parent, pde) <0 ) { +kfree (pde); +return NULL ; + } +return pde; +} +struct proc_dir_entry *proc_mkdir_mode (const char *name,mode_t mode,struct proc_dir_entry *parent) {
fs/proc/proc_net.c +30 -1
@@ -191,9 +191,30 @@ void proc_net_remove(struct net *net, const char *name) }EXPORT_SYMBOL_GPL (proc_net_remove); +static int net_id_readlink (struct dentry *dentry,char __user *buf,int buflen) +{ +struct net *net; +char kbuf[42 ]; +int len; + + net =get_proc_net (dentry->d_inode); +if (!net) +return -ENXIO ; + len =snprintf (kbuf,sizeof (kbuf),"%u" , net->id); +put_net (net); + len =min (len, buflen); +if (copy_to_user (buf, kbuf, len)) +return -EFAULT ; +return len; +} + +static const struct inode_operations net_id_proc_iops = { + .readlink = net_id_readlink, +}; +static __net_init int proc_net_ns_init (struct net *net) { -struct proc_dir_entry *netd, *net_statd; +struct proc_dir_entry *netd, *net_statd, *pde;int err; err = -ENOMEM ;
@@ -214,8 +235,15 @@ static __net_init int proc_net_ns_init(struct net *net) net->proc_net = netd; net->proc_net_stat = net_statd; + + pde =_proc_symlink ("id" , net->proc_net, &net_id_proc_iops); +if (!pde) + goto free_net_stat; +return 0 ; +free_net_stat: +kfree (net_statd); free_net:kfree (netd); out:
@@ -224,6 +252,7 @@ out: static __net_exit void proc_net_ns_exit(struct net *net) { +remove_proc_entry ("id" , net->proc_net);remove_proc_entry ("stat" , net->proc_net);kfree (net->proc_net); }
include/linux/proc_fs.h +7 -0
@@ -143,6 +143,7 @@ extern void proc_device_tree_update_prop(struct proc_dir_entry *pde, struct property *oldprop);#endif /* CONFIG_PROC_DEVICETREE */ +struct proc_dir_entry *_proc_symlink (const char *name,struct proc_dir_entry *parent,const struct inode_operations *proc_iops);extern struct proc_dir_entry *proc_symlink (const char *,struct proc_dir_entry *,const char *);extern struct proc_dir_entry *proc_mkdir (const char *,struct proc_dir_entry *);
@@ -204,8 +205,14 @@ static inline struct proc_dir_entry *proc_create_data(const char *name, }#define remove_proc_entry (name, parent) do {} while (0) +static inline struct proc_dir_entry *_proc_symlink (const char *name,struct proc_dir_entry *parent,const struct inode_operations *proc_iops) +{ +return NULL ; +} +static inline struct proc_dir_entry *proc_symlink (const char *name,struct proc_dir_entry *parent,const char *dest) {return NULL ;} +static inline struct proc_dir_entry *proc_mkdir (const char *name,struct proc_dir_entry *parent) {return NULL ;} static inline struct proc_dir_entry *proc_mkdir_mode(const char *name,
include/net/net_namespace.h +10 -0
@@ -96,6 +96,16 @@ struct net {struct netns_xfrm xfrm;#endif struct netns_ipvs *ipvs; + +/* +* netns unique id solely for userspace consumption, +* see /proc/net/id symlink. +* +* init_net has id 0. +* +* Write-once field. +*/ +unsigned int id; };
net/core/net_namespace.c +54 -0
78 lines not on this page · raw message
Quoted 3 lines · Alexey Dobriyan
David Lamparter pointed some real scenarios where knowing if two processes live in same netns is important, like "how do I kill _all_ processes in netns to shutdown it".
Currently today the way I do this is md5sum /proc/<pid>/mounts.
That works because it is usually necessary to have a separate mount namespace with a separate set of mounts to accommodate sysfs.
Quoted 3 lines
Currently only kernel knows if two netns are the same. Userspace maybe can look at different proc files to find a match indirectly sysconf-style but result will be ugly no matter what.
Somewhat.
Right now today without patches if we limit ourselves to the network namespace there is a pretty valid way to do this.
stat /proc/<pid>/net/dev and compare the inode numbers.
Or any other file in /proc/*/net/. The inode numbers are the same if you are in the same network namespace.
Quoted 8 lines
Add
/proc/*/net/idsymlink which "points" to an integer.$ readlink /proc/net/id 0$ readlink /proc/2941/net/id 1
Quoted 8 lines
"id" is not a file because 1 syscall is faster than 3 syscalls.
The only rules and expectations for userspace are:
[as if they will comply, ha-ha]*
init_netalways has id 0
* two netns do not have same id
* id is unsigned integer
I don't like this patch because we already have a proc interface that already solves this in production kernels today.
- stat is a single syscall
- two netns do not have the same id
- id is an ino_t.
Now it probably needs to be better documented that /proc/*/net/* have the same inode number if the network namespace is the same, as everyone including myself overlooked this very handy existing property.
Writing this it occurs to me there is a misfeature in my pending namespace file descriptor code. Right now /proc/<pid>/ns/net has a floating inode number and it would be good if I could make that a inode number be the same for every file that refers to the same network namespace. Ugh.
Eric
Quoted 11 lines · Eric W. Biederman
Quoted 3 lines · Alexey Dobriyan
*
init_netalways has id 0
* two netns do not have same id
* id is unsigned integerI don't like this patch because we already have a proc interface that already solves this in production kernels today.
- stat is a single syscall
- two netns do not have the same id
- id is anino_t.
Yeah, stat /proc/*/net/dev works.
If you document this, it means we can't change the way ->low_ino is set. And we can't do other things inside irregular part of procfs.
But can we add clean interface once in a while.
Adding the containers list.
Quoted 9 lines · Alexey Dobriyan
Quoted 8 lines · Eric W. Biederman
Quoted 3 lines · Alexey Dobriyan
*
init_netalways has id 0
* two netns do not have same id
* id is unsigned integerI don't like this patch because we already have a proc interface that already solves this in production kernels today.
Quoted 7 lines
Quoted 3 lines
- stat is a single syscall
- two netns do not have the same id
- id is anino_t.Yeah, stat
/proc/*/net/devworks.
If you document this, it means we can't change the way ->low_ino is set. And we can't do other things inside irregular part of procfs.
Maybe. Certainly there are things that would suggest we need some fixes to this part of procfs.
Quoted 1 line
But can we add clean interface once in a while.
I am all for making a clean solution. I don't see a proc file in in /proc/net that provides a small integer as particularly clean.
It has the classic problem of what namespace are namespaces named in. It only solves the problem for the network namespace.
So on that level I really like the idea of inode numbers in proc being the place where we have a name. People generally don't get confused about inode numbers understanding they are an implementation detail but they do understand that inode numbers plus filesystem information can be used to compare files for identity.
So let's skip the fact that /proc/*/net/dev happens to work for a moment.
For clean interfaces I am in the process of adding /proc/<pid>/ns/net, /proc/<pid>/ns/ipc, and /proc/<pid>/ns/uts.
If we can make those files inode number be the same if the namespace is the same like /proc/<pid>/net/dev is today. I think we will have a clean solution.
Additionally that solution will work for comparing network namespaces that don't happen to have any processes in them at the moment. Because fstat works on file descriptors.
With the /proc/<pid>/ns/net file and bind mounts I have solved the deeper problem of how do we get userspace policy into the naming of namespaces. With those files and the setns system call I have solved the other problem of what is a good way to refer to namespaces without assuming a global name. So once those changes are merged I expect there to be much less pressure to misuse any kind of identifier we can have.
And if we only make the guarantee about inode consistency for the /proc/<pid>/ns/FILE files I don't expect it will make maintenance of procfs any harder than it already is.
Eric
Quoted 5 lines
... Eric W. Biederman wrote:
Now it probably needs to be better documented that/proc/*/net/*have the same inode number if the network namespace is the same, as everyone including myself overlooked this very handy existing property.
Eh, so did I. But, yes, very nice.
Quoted 3 lines · Eric W. Biederman
Additionally that solution will work for comparing network namespaces that don't happen to have any processes in them at the moment. Because fstat works on file descriptors.
Hm. I have a peeve here. Assume I am a... rogue admin, whatever. I have root on a router. I create a new network namespace, put a macvlan of eth0 in it and a macvlan of eth1. I enable ip_forward.
Then I make a mount namespace, bind-mount the net namespace, bind mount the mount namespace and terminate all processes that reference it (yes this does work, i just checked [!]).
Now I can use it to bypass all firewall rules, IDS, whatever.
How is any normal admin, monitoring script or whatever else able to detect this?
Quoted 10 lines
With the /proc/<pid>/ns/net file and bind mounts I have solved the deeper problem of how do we get userspace policy into the naming of namespaces. With those files and the setns system call I have solved the other problem of what is a good way to refer to namespaces without assuming a global name. So once those changes are merged I expect there to be much less pressure to misuse any kind of identifier we can have.
And if we only make the guarantee about inode consistency for the /proc/<pid>/ns/FILE files I don't expect it will make maintenance of procfs any harder than it already is.
-David
Quoted 3 lines · David Lamparter
Then I make a mount namespace, bind-mount the net namespace, bind mount the mount namespace and terminate all processes that reference it (yes this does work, i just checked [!]).
Actually, Eric, bind-mounting a mount namespace inside itself should probably be forbidden? No idea if you changed that (running a year-old version of your patches here). Not only can you lose network namespaces inside those self-referential mount namespaces but also references to block devices, unix socket connections, etc. pp.
-David
PATCH 1/2 proc: Generalize proc inode allocation
Generalize the proc inode allocation so that it can be used without having to having to create a proc_dir_entry.
This will allow namespace file descriptors to remain light weight entitities but still have the same inode number when the backing namespace is the same.
---
Baring problems in review I plan to merge these patches via my linux-2.6-nsfd tree.
fs/proc/generic.c | 26 +++++++++++++------------- include/linux/proc_fs.h | 10 ++++++++++ 2 files changed, 23 insertions(+), 13 deletions(-)
fs/proc/generic.c +13 -13
@@ -350,14 +350,14 @@ static DEFINE_SPINLOCK(proc_inum_lock); /* protects the above */* Return an inode number between PROC_DYNAMIC_FIRST and * 0xffffffff, or zero on failure. */ -static unsigned int get_inode_number (void ) +int proc_alloc_inum (unsigned int *inum) {unsigned int i;int error; retry: -if (ida_pre_get (&proc_inum_ida,GFP_KERNEL ) ==0 ) -return 0 ; +if (!ida_pre_get (&proc_inum_ida,GFP_KERNEL )) +return -ENOMEM ;spin_lock (&proc_inum_lock); error =ida_get_new (&proc_inum_ida, &i);
@@ -365,18 +365,19 @@ retry:if (error == -EAGAIN ) goto retry;else if (error) -return 0 ; +return error;if (i >UINT_MAX -PROC_DYNAMIC_FIRST ) {spin_lock (&proc_inum_lock);ida_remove (&proc_inum_ida, i);spin_unlock (&proc_inum_lock); -return 0 ; +return -ENOSPC ; } -return PROC_DYNAMIC_FIRST + i; + *inum =PROC_DYNAMIC_FIRST + i; +return 0 ; } -static void release_inode_number (unsigned int inum) +void proc_free_inum (unsigned int inum) {spin_lock (&proc_inum_lock);ida_remove (&proc_inum_ida, inum -PROC_DYNAMIC_FIRST );
@@ -554,13 +555,12 @@ static const struct inode_operations proc_dir_inode_operations = {static int proc_register (struct proc_dir_entry * dir,struct proc_dir_entry * dp) { -unsigned int i;struct proc_dir_entry *tmp; +int ret; - i =get_inode_number (); -if (i ==0 ) -return -EAGAIN ; - dp->low_ino = i; + ret =proc_alloc_inum (&dp->low_ino); +if (ret) +return ret;if (S_ISDIR (dp->mode)) {if (dp->proc_iops ==NULL ) {
@@ -766,7 +766,7 @@ EXPORT_SYMBOL(proc_create_data);static void free_proc_entry (struct proc_dir_entry *de) { -release_inode_number (de->low_ino); +proc_free_inum (de->low_ino);if (S_ISLNK (de->mode))kfree (de->data);
include/linux/proc_fs.h +10 -0
@@ -175,6 +175,8 @@ extern struct proc_dir_entry *proc_net_mkdir(struct net *net, const char *name,extern struct file *proc_ns_fget (int fd); +extern int proc_alloc_inum (unsigned int *pino); +extern void proc_free_inum (unsigned int inum);#else #define proc_net_fops_create (net, name, mode, fops) ({ (void)(mode), NULL; })
@@ -229,6 +231,14 @@ static inline struct file *proc_ns_fget(int fd) return ERR_PTR(-EINVAL); } +static inline int proc_alloc_inum (unsigned int *inum) +{ + *inum =1 ; +return 0 ; +} +static inline void proc_free_inum (unsigned int inum) +{ +}#endif /* CONFIG_PROC_FS */ #if !defined(CONFIG_PROC_KCORE )
Signature · 1 line
1.7.5.1.217.g4e3aa
PATCH 2/2 proc: Usable inode numbers for the namespace file descriptors.
Assign a unique proc inode to each namespace, yielding an identifier that userspace can use for identifying a namespace.
This has been a long requested feature and only blocked because a naive implementation would put the id in a global space and would ultimately require having a namespace for the names of namespaces, making migration and certain virtualization tricks impossible.
We still don't have per superblock inode numbers for proc, which appears necessary for application unaware checkpoint/restart and migrations (if the application is using namespace filedescriptors) but that is now allowd by the design if it becomes important.
I have preallocated the ipc and uts initial proc inode numbers so their structures can be statically initialized.
--- fs/proc/namespaces.c | 1 + include/linux/ipc_namespace.h | 2 ++ include/linux/proc_fs.h | 4 ++++ include/linux/utsname.h | 1 + include/net/net_namespace.h | 2 ++ init/version.c | 2 ++ ipc/msgutil.c | 2 ++ ipc/namespace.c | 16 ++++++++++++++++ kernel/utsname.c | 17 ++++++++++++++++- net/core/net_namespace.c | 24 ++++++++++++++++++++++++ 10 files changed, 70 insertions(+), 1 deletions(-)
fs/proc/namespaces.c +1 -0
@@ -54,6 +54,7 @@ static struct dentry *proc_ns_instantiate(struct inode *dir, ei->ns_ops = ns_ops; ei->ns = ns; + inode->i_ino = ns_ops->inum (ei->ns); dentry->d_op = &pid_dentry_operations;d_add (dentry, inode);/* Close the race of the process dying before we return the dentry */
include/linux/ipc_namespace.h +2 -0
@@ -60,6 +60,8 @@ struct ipc_namespace {/* user_ns which owns the ipc ns */ struct user_namespace *user_ns; + +unsigned int proc_inum; };extern struct ipc_namespace init_ipc_ns;
include/linux/proc_fs.h +4 -0
@@ -29,8 +29,11 @@ struct mm_struct;enum {PROC_ROOT_INO =1 , +PROC_IPC_INIT_INO =2 , +PROC_UTS_INIT_INO =3 , }; +/* * This is not completely implemented yet. The idea is to * create an in-memory tree (like the actual /proc filesystem
@@ -257,6 +260,7 @@ struct proc_ns_operations {void *(*get)(struct task_struct *task);void (*put)(void *ns);int (*install)(struct nsproxy *nsproxy,void *ns); +unsigned int (*inum)(void *ns); };extern const struct proc_ns_operations netns_operations;extern const struct proc_ns_operations utsns_operations;
include/linux/utsname.h +1 -0
@@ -44,6 +44,7 @@ struct uts_namespace {struct kref kref;struct new_utsname name;struct user_namespace *user_ns; +unsigned int proc_inum; };extern struct uts_namespace init_uts_ns;
include/net/net_namespace.h +2 -0
@@ -49,6 +49,8 @@ struct net {struct list_head cleanup_list;/* namespaces on death row */ struct list_head exit_list;/* Use only net_mutex */ +unsigned int proc_inum; +struct proc_dir_entry *proc_net;struct proc_dir_entry *proc_net_stat;
init/version.c +2 -0
@@ -12,6 +12,7 @@#include <linux/utsname.h> #include <generated/utsrelease.h> #include <linux/version.h> +#include <linux/proc_fs.h> #ifndef CONFIG_KALLSYMS #define version (a) Version_ ## a
@@ -34,6 +35,7 @@ struct uts_namespace init_uts_ns = { .domainname = UTS_DOMAINNAME, }, .user_ns = &init_user_ns, + .proc_inum = PROC_UTS_INIT_INO, };EXPORT_SYMBOL_GPL (init_uts_ns);
ipc/msgutil.c +2 -0
@@ -14,6 +14,7 @@#include <linux/slab.h> #include <linux/ipc.h> #include <linux/ipc_namespace.h> +#include <linux/proc_fs.h> #include <asm/uaccess.h> #include "util.h"
@@ -33,6 +34,7 @@ struct ipc_namespace init_ipc_ns = { .mq_msgsize_max = DFLT_MSGSIZEMAX, #endif .user_ns = &init_user_ns, + .proc_inum = PROC_IPC_INIT_INO, };atomic_t nr_ipc_ns =ATOMIC_INIT (1 );
ipc/namespace.c +16 -0
@@ -26,9 +26,16 @@ static struct ipc_namespace *create_ipc_ns(struct task_struct *tsk,if (ns ==NULL )return ERR_PTR (-ENOMEM ); + err =proc_alloc_inum (&ns->proc_inum); +if (err) { +kfree (ns); +return ERR_PTR (err); + } +atomic_set (&ns->count,1 ); err =mq_init_ns (ns);if (err) { +proc_free_inum (ns->proc_inum);kfree (ns);return ERR_PTR (err); }
@@ -113,6 +120,7 @@ static void free_ipc_ns(struct ipc_namespace *ns)*/ ipcns_notify (IPCNS_REMOVED );put_user_ns (ns->user_ns); +proc_free_inum (ns->proc_inum);kfree (ns); }
@@ -170,10 +178,18 @@ static int ipcns_install(struct nsproxy *nsproxy, void *ns)return 0 ; } +static unsigned int ipcns_inum (void *vp) +{ +struct ipc_namespace *ns = vp; + +return ns->proc_inum; +} +const struct proc_ns_operations ipcns_operations = { .name ="ipc" , .type =CLONE_NEWIPC , .get = ipcns_get, .put = ipcns_put, .install = ipcns_install, + .inum = ipcns_inum, };
kernel/utsname.c +16 -1
@@ -36,11 +36,18 @@ static struct uts_namespace *clone_uts_ns(struct task_struct *tsk, struct uts_namespace *old_ns) {struct uts_namespace *ns; +int err; ns =create_uts_ns ();if (!ns)return ERR_PTR (-ENOMEM ); + err =proc_alloc_inum (&ns->proc_inum); +if (err) { +kfree (ns); +return ERR_PTR (err); + } +down_read (&uts_sem);memcpy (&ns->name, &old_ns->name,sizeof (ns->name)); ns->user_ns =get_user_ns (task_cred_xxx (tsk, user)->user_ns);
@@ -78,6 +85,7 @@ void free_uts_ns(struct kref *kref) ns = container_of(kref, struct uts_namespace, kref);put_user_ns (ns->user_ns); +proc_free_inum (ns->proc_inum);kfree (ns); }
@@ -110,11 +118,18 @@ static int utsns_install(struct nsproxy *nsproxy, void *ns)return 0 ; } +static unsigned int utsns_inum (void *vp) +{ +struct uts_namespace *ns = vp; + +return ns->proc_inum; +} +const struct proc_ns_operations utsns_operations = { .name ="uts" , .type =CLONE_NEWUTS , .get = utsns_get, .put = utsns_put, .install = utsns_install, + .inum = utsns_inum, }; -
net/core/net_namespace.c +24 -0
@@ -358,6 +358,21 @@ struct net *get_net_ns_by_pid(pid_t pid) }EXPORT_SYMBOL_GPL (get_net_ns_by_pid); +static __net_init int net_ns_net_init (struct net *net) +{ +return proc_alloc_inum (&net->proc_inum); +} + +static __net_exit void net_ns_net_exit(struct net *net) +{ +proc_free_inum (net->proc_inum); +} + +static struct pernet_operations __net_initdata net_ns_ops = { + .init = net_ns_net_init, + .exit = net_ns_net_exit, +}; +static int __init net_ns_init (void ) {struct net_generic *ng;
@@ -389,6 +404,8 @@ static int __init net_ns_init(void)mutex_unlock (&net_mutex); +register_pernet_subsys (&net_ns_ops); +return 0 ; }
@@ -616,11 +633,18 @@ static int netns_install(struct nsproxy *nsproxy, void *ns) return 0; } +static unsigned int netns_inum (void *ns) +{ +struct net *net = ns; +return net->proc_inum; +} +const struct proc_ns_operations netns_operations = { .name ="net" , .type =CLONE_NEWNET , .get = netns_get, .put = netns_put, .install = netns_install, + .inum = netns_inum, };#endif
Signature · 1 line
1.7.5.1.217.g4e3aa
Re: [PATCH 2/2] proc: Usable inode numbers for the namespace file descriptors.
From: [email protected] (Eric W. Biederman)
Date: Fri, 17 Jun 2011 16:33:19 -0700
Quoted 15 lines
Assign a unique proc inode to each namespace, yielding an identifier that userspace can use for identifying a namespace.
This has been a long requested feature and only blocked because a naive implementation would put the id in a global space and would ultimately require having a namespace for the names of namespaces, making migration and certain virtualization tricks impossible.
We still don't have per superblock inode numbers for proc, which appears necessary for application unaware checkpoint/restart and migrations (if the application is using namespace filedescriptors) but that is now allowd by the design if it becomes important.
Quoted 4 lines
I have preallocated the ipc and uts initial proc inode numbers so their structures can be statically initialized.
Signed-off-by Eric W. Biederman <[email protected]>
For networking bits:
Re: [PATCH 2/2] proc: Usable inode numbers for the namespace file descriptors.
In reply to Eric W. BiedermanQuoted 15 lines · Eric W. Biederman
Assign a unique proc inode to each namespace, yielding an identifier that userspace can use for identifying a namespace.
This has been a long requested feature and only blocked because a naive implementation would put the id in a global space and would ultimately require having a namespace for the names of namespaces, making migration and certain virtualization tricks impossible.
We still don't have per superblock inode numbers for proc, which appears necessary for application unaware checkpoint/restart and migrations (if the application is using namespace filedescriptors) but that is now allowd by the design if it becomes important.
Quoted 4 lines
I have preallocated the ipc and uts initial proc inode numbers so their structures can be statically initialized.
Signed-off-by Eric W. Biederman <[email protected]>
I've not looked at the setns patches enough, but from what I can see here it looks good.
thanks,
-serge
Quoted 283 lines
--- fs/proc/namespaces.c | 1 + include/linux/ipc_namespace.h | 2 ++ include/linux/proc_fs.h | 4 ++++ include/linux/utsname.h | 1 + include/net/net_namespace.h | 2 ++ init/version.c | 2 ++ ipc/msgutil.c | 2 ++ ipc/namespace.c | 16 ++++++++++++++++ kernel/utsname.c | 17 ++++++++++++++++- net/core/net_namespace.c | 24 ++++++++++++++++++++++++ 10 files changed, 70 insertions(+), 1 deletions(-)fs/proc/namespaces.c +1
-0@@ -54,6 +54,7 @@ static struct dentry *proc_ns_instantiate(struct inode *dir, ei->ns_ops = ns_ops; ei->ns = ns; + inode->i_ino = ns_ops->inum (ei->ns); dentry->d_op = &pid_dentry_operations;d_add (dentry, inode);/* Close the race of the process dying before we return the dentry */ include/linux/ipc_namespace.h +2
-0@@ -60,6 +60,8 @@ struct ipc_namespace {/* user_ns which owns the ipc ns */ struct user_namespace *user_ns; + +unsigned int proc_inum; };extern struct ipc_namespace init_ipc_ns;include/linux/proc_fs.h +4
-0@@ -29,8 +29,11 @@ struct mm_struct;enum {PROC_ROOT_INO =1 , +PROC_IPC_INIT_INO =2 , +PROC_UTS_INIT_INO =3 , }; +/* * This is not completely implemented yet. The idea is to * create an in-memory tree (like the actual /proc filesystem @@ -257,6 +260,7 @@ struct proc_ns_operations {void *(*get)(struct task_struct *task);void (*put)(void *ns);int (*install)(struct nsproxy *nsproxy,void *ns); +unsigned int (*inum)(void *ns); };extern const struct proc_ns_operations netns_operations;extern const struct proc_ns_operations utsns_operations;include/linux/utsname.h +1
-0@@ -44,6 +44,7 @@ struct uts_namespace {struct kref kref;struct new_utsname name;struct user_namespace *user_ns; +unsigned int proc_inum; };extern struct uts_namespace init_uts_ns;include/net/net_namespace.h +2
-0@@ -49,6 +49,8 @@ struct net {struct list_head cleanup_list;/* namespaces on death row */ struct list_head exit_list;/* Use only net_mutex */ +unsigned int proc_inum; +struct proc_dir_entry *proc_net;struct proc_dir_entry *proc_net_stat;init/version.c +2
-0@@ -12,6 +12,7 @@#include <linux/utsname.h> #include <generated/utsrelease.h> #include <linux/version.h> +#include <linux/proc_fs.h> #ifndef CONFIG_KALLSYMS #define version (a) Version_ ## a@@ -34,6 +35,7 @@ struct uts_namespace init_uts_ns = { .domainname = UTS_DOMAINNAME, }, .user_ns = &init_user_ns, + .proc_inum = PROC_UTS_INIT_INO, };EXPORT_SYMBOL_GPL (init_uts_ns);ipc/msgutil.c +2
-0@@ -14,6 +14,7 @@#include <linux/slab.h> #include <linux/ipc.h> #include <linux/ipc_namespace.h> +#include <linux/proc_fs.h> #include <asm/uaccess.h> #include "util.h" @@ -33,6 +34,7 @@ struct ipc_namespace init_ipc_ns = { .mq_msgsize_max = DFLT_MSGSIZEMAX, #endif .user_ns = &init_user_ns, + .proc_inum = PROC_IPC_INIT_INO, };atomic_t nr_ipc_ns =ATOMIC_INIT (1 );ipc/namespace.c +16
-0@@ -26,9 +26,16 @@ static struct ipc_namespace *create_ipc_ns(struct task_struct *tsk,if (ns ==NULL )return ERR_PTR (-ENOMEM ); + err =proc_alloc_inum (&ns->proc_inum); +if (err) { +kfree (ns); +return ERR_PTR (err); + } +atomic_set (&ns->count,1 ); err =mq_init_ns (ns);if (err) { +proc_free_inum (ns->proc_inum);kfree (ns);return ERR_PTR (err); }@@ -113,6 +120,7 @@ static void free_ipc_ns(struct ipc_namespace *ns)*/ ipcns_notify (IPCNS_REMOVED );put_user_ns (ns->user_ns); +proc_free_inum (ns->proc_inum);kfree (ns); }@@ -170,10 +178,18 @@ static int ipcns_install(struct nsproxy *nsproxy, void *ns)return 0 ; } +static unsigned int ipcns_inum (void *vp) +{ +struct ipc_namespace *ns = vp; + +return ns->proc_inum; +} +const struct proc_ns_operations ipcns_operations = { .name ="ipc" , .type =CLONE_NEWIPC , .get = ipcns_get, .put = ipcns_put, .install = ipcns_install, + .inum = ipcns_inum, };kernel/utsname.c +16
-1@@ -36,11 +36,18 @@ static struct uts_namespace *clone_uts_ns(struct task_struct *tsk, struct uts_namespace *old_ns) {struct uts_namespace *ns; +int err; ns =create_uts_ns ();if (!ns)return ERR_PTR (-ENOMEM ); + err =proc_alloc_inum (&ns->proc_inum); +if (err) { +kfree (ns); +return ERR_PTR (err); + } +down_read (&uts_sem);memcpy (&ns->name, &old_ns->name,sizeof (ns->name)); ns->user_ns =get_user_ns (task_cred_xxx (tsk, user)->user_ns);@@ -78,6 +85,7 @@ void free_uts_ns(struct kref *kref) ns = container_of(kref, struct uts_namespace, kref);put_user_ns (ns->user_ns); +proc_free_inum (ns->proc_inum);kfree (ns); }@@ -110,11 +118,18 @@ static int utsns_install(struct nsproxy *nsproxy, void *ns)return 0 ; } +static unsigned int utsns_inum (void *vp) +{ +struct uts_namespace *ns = vp; + +return ns->proc_inum; +} +const struct proc_ns_operations utsns_operations = { .name ="uts" , .type =CLONE_NEWUTS , .get = utsns_get, .put = utsns_put, .install = utsns_install, + .inum = utsns_inum, }; -net/core/net_namespace.c +24
-0@@ -358,6 +358,21 @@ struct net *get_net_ns_by_pid(pid_t pid) }EXPORT_SYMBOL_GPL (get_net_ns_by_pid); +static __net_init int net_ns_net_init (struct net *net) +{ +return proc_alloc_inum (&net->proc_inum); +} + +static __net_exit void net_ns_net_exit(struct net *net) +{ +proc_free_inum (net->proc_inum); +} + +static struct pernet_operations __net_initdata net_ns_ops = { + .init = net_ns_net_init, + .exit = net_ns_net_exit, +}; +static int __init net_ns_init (void ) {struct net_generic *ng;@@ -389,6 +404,8 @@ static int __init net_ns_init(void)mutex_unlock (&net_mutex); +register_pernet_subsys (&net_ns_ops); +return 0 ; }@@ -616,11 +633,18 @@ static int netns_install(struct nsproxy *nsproxy, void *ns) return 0; } +static unsigned int netns_inum (void *ns) +{ +struct net *net = ns; +return net->proc_inum; +} +const struct proc_ns_operations netns_operations = { .name ="net" , .type =CLONE_NEWNET , .get = netns_get, .put = netns_put, .install = netns_install, + .inum = netns_inum, };#endif Signature · 1 line
1.7.5.1.217.g4e3aa
Re: [PATCH 2/2] proc: Usable inode numbers for the namespace file descriptors.
Quoted 19 lines · Serge E. Hallyn
Quoted 18 lines · Eric W. Biederman
Assign a unique proc inode to each namespace, yielding an identifier that userspace can use for identifying a namespace.
This has been a long requested feature and only blocked because a naive implementation would put the id in a global space and would ultimately require having a namespace for the names of namespaces, making migration and certain virtualization tricks impossible.
We still don't have per superblock inode numbers for proc, which appears necessary for application unaware checkpoint/restart and migrations (if the application is using namespace filedescriptors) but that is now allowd by the design if it becomes important.
I have preallocated the ipc and uts initial proc inode numbers so their structures can be statically initialized.
Quoted 6 lines
Quoted 1 line
Signed-off-by Eric W. Biederman <[email protected]>I've not looked at the setns patches enough, but from what I can see here it looks good.
Acked-by Serge Hallyn <[email protected]>
Thanks.
There are bugs in my existing proc bits that I am working on fixing so this second patch will have a small update, before it gets merged.
But posting the patches I was looking for a little review and I was announcing I had solved the technical problem of how we talk about namespaces, without needing to introduce another namespace for namespaces.
Eric
Re: [PATCH 1/2] proc: Generalize proc inode allocation
In reply to Eric W. BiedermanQuoted 9 lines · Eric W. Biederman
Generalize the proc inode allocation so that it can be used without having to having to create a
proc_dir_entry.This will allow namespace file descriptors to remain light weight entitities but still have the same inode number when the backing namespace is the same.
Signed-off-by Eric W. Biederman <[email protected]>
Quoted 113 lines
---
Baring problems in review I plan to merge these patches via my linux-2.6-nsfd tree.
fs/proc/generic.c | 26 +++++++++++++------------- include/linux/proc_fs.h | 10 ++++++++++ 2 files changed, 23 insertions(+), 13 deletions(-)fs/proc/generic.c +13
-13@@ -350,14 +350,14 @@ static DEFINE_SPINLOCK(proc_inum_lock); /* protects the above */* Return an inode number between PROC_DYNAMIC_FIRST and * 0xffffffff, or zero on failure. */ -static unsigned int get_inode_number (void ) +int proc_alloc_inum (unsigned int *inum) {unsigned int i;int error; retry: -if (ida_pre_get (&proc_inum_ida,GFP_KERNEL ) ==0 ) -return 0 ; +if (!ida_pre_get (&proc_inum_ida,GFP_KERNEL )) +return -ENOMEM ;spin_lock (&proc_inum_lock); error =ida_get_new (&proc_inum_ida, &i);@@ -365,18 +365,19 @@ retry:if (error == -EAGAIN ) goto retry;else if (error) -return 0 ; +return error;if (i >UINT_MAX -PROC_DYNAMIC_FIRST ) {spin_lock (&proc_inum_lock);ida_remove (&proc_inum_ida, i);spin_unlock (&proc_inum_lock); -return 0 ; +return -ENOSPC ; } -return PROC_DYNAMIC_FIRST + i; + *inum =PROC_DYNAMIC_FIRST + i; +return 0 ; } -static void release_inode_number (unsigned int inum) +void proc_free_inum (unsigned int inum) {spin_lock (&proc_inum_lock);ida_remove (&proc_inum_ida, inum -PROC_DYNAMIC_FIRST );@@ -554,13 +555,12 @@ static const struct inode_operations proc_dir_inode_operations = {static int proc_register (struct proc_dir_entry * dir,struct proc_dir_entry * dp) { -unsigned int i;struct proc_dir_entry *tmp; +int ret; - i =get_inode_number (); -if (i ==0 ) -return -EAGAIN ; - dp->low_ino = i; + ret =proc_alloc_inum (&dp->low_ino); +if (ret) +return ret;if (S_ISDIR (dp->mode)) {if (dp->proc_iops ==NULL ) {@@ -766,7 +766,7 @@ EXPORT_SYMBOL(proc_create_data);static void free_proc_entry (struct proc_dir_entry *de) { -release_inode_number (de->low_ino); +proc_free_inum (de->low_ino);if (S_ISLNK (de->mode))kfree (de->data);include/linux/proc_fs.h +10
-0@@ -175,6 +175,8 @@ extern struct proc_dir_entry *proc_net_mkdir(struct net *net, const char *name,extern struct file *proc_ns_fget (int fd); +extern int proc_alloc_inum (unsigned int *pino); +extern void proc_free_inum (unsigned int inum);#else #define proc_net_fops_create (net, name, mode, fops) ({ (void)(mode), NULL; })@@ -229,6 +231,14 @@ static inline struct file *proc_ns_fget(int fd) return ERR_PTR(-EINVAL); } +static inline int proc_alloc_inum (unsigned int *inum) +{ + *inum =1 ; +return 0 ; +} +static inline void proc_free_inum (unsigned int inum) +{ +}#endif /* CONFIG_PROC_FS */ #if !defined(CONFIG_PROC_KCORE )Signature · 1 line
1.7.5.1.217.g4e3aa
Quoted 13 lines · David Lamparter
Quoted 5 lines
... Eric W. Biederman wrote:
Now it probably needs to be better documented that/proc/*/net/*have the same inode number if the network namespace is the same, as everyone including myself overlooked this very handy existing property.Eh, so did I. But, yes, very nice.
Quoted 3 lines · Eric W. Biederman
Additionally that solution will work for comparing network namespaces that don't happen to have any processes in them at the moment. Because fstat works on file descriptors.
Quoted 7 lines
Hm. I have a peeve here. Assume I am a... rogue admin, whatever. I have root on a router. I create a new network namespace, put a macvlan of eth0 in it and a macvlan of eth1. I enable
ip_forward.Then I make a mount namespace, bind-mount the net namespace, bind mount the mount namespace and terminate all processes that reference it (yes this does work, i just checked [!]).
You must be using an older version of my patchset than what I have queued for Linus. Bind mounting the mount namepsace and creating reference counting loops is a weird and ugly case. So for the moment I am not supporting the mount namespace, until I can think through the consequences.
Quoted 4 lines
Now I can use it to bypass all firewall rules, IDS, whatever.
How is any normal admin, monitoring script or whatever else able to detect this?
Which is why we I proceed slowly and cautiously with adding new kernel interfaces. It is hard to think of everything until you can actually put it into use, and play with it.
Other than not allowing bind mounting the mount namespace I don't have any all encompassing really good answers at the moment.
I do have a few small answers. For network namespaces you can look in /proc/slabinfo and see how many you have, unless slub is lying to you. On the switch your server is connected to you can look at the mac table and see which mac addresses are currently in use, and notice if there are unaccounted for mac addresses.
Eric