Wed, 01 May 2013 21:38:02 -0300
Hi,
I ran into an issue where /proc/net entries
were missing in guest vservers using network
namespaces.
A quick search revealed that this issue was
already reported:
http://www.paul.sladen.org/vserver/archives/201206/0020.html
But apparently no solution came out.
I wrote a patch that fixes this issue, so that programs like netstat,
ifconfig, etc., work as
expected.
This patch is a big fat kludge. Although I believe
it fixes the problem hopefully without introducing
new ones, it is almost certainly not the best
approach -- since I'm a total newbie about vserver
kernel hacking and this is the product of one
single afternoon of delving through the relevant
code, I can't really be sure there are no side
effects. So it is meant only as a starting
point for discussions about how to properly fix
this issue. Use at your own risk.
I would very much appreciate suggestions from
more experienced coders on the proper approach
to fix this.
Best,
-Marco "Kiko" Carnut // melbar
diff -bur linux-3.2.9-vs2.3.2.7/fs/proc/generic.c linux-3.2.9-vs2.3.2.7-melbar/fs/proc/generic.c
--- linux-3.2.9-vs2.3.2.7/fs/proc/generic.c 2012-03-10 10:34:52.000000000 -0300
+++ linux-3.2.9-vs2.3.2.7-melbar/fs/proc/generic.c 2013-05-01 20:29:03.834308882 -0300
@@ -23,6 +23,7 @@
#include <linux/spinlock.h>
#include <linux/completion.h>
#include <linux/vserver/inode.h>
+#include <net/net_namespace.h>
#include <asm/uaccess.h>
#include "internal.h"
@@ -606,7 +607,8 @@
static struct proc_dir_entry *__proc_create(struct proc_dir_entry **parent,
const char *name,
mode_t mode,
- nlink_t nlink)
+ nlink_t nlink,
+ int vx_flags)
{
struct proc_dir_entry *ent = NULL;
const char *fn = name;
@@ -634,7 +636,7 @@
ent->nlink = nlink;
atomic_set(&ent->count, 1);
ent->pde_users = 0;
- ent->vx_flags = IATTR_PROC_DEFAULT;
+ ent->vx_flags = vx_flags;
spin_lock_init(&ent->pde_unload_lock);
ent->pde_unload_completion = NULL;
INIT_LIST_HEAD(&ent->pde_openers);
@@ -648,7 +650,7 @@
struct proc_dir_entry *ent;
ent = __proc_create(&parent, name,
- (S_IFLNK | S_IRUGO | S_IWUGO | S_IXUGO),1);
+ (S_IFLNK | S_IRUGO | S_IWUGO | S_IXUGO),1,IATTR_PROC_DEFAULT);
if (ent) {
ent->data = kmalloc((ent->size=strlen(dest))+1, GFP_KERNEL);
@@ -674,7 +676,7 @@
{
struct proc_dir_entry *ent;
- ent = __proc_create(&parent, name, S_IFDIR | mode, 2);
+ ent = __proc_create(&parent, name, S_IFDIR | mode, 2, IATTR_PROC_DEFAULT);
if (ent) {
if (proc_register(parent, ent) < 0) {
kfree(ent);
@@ -690,7 +692,7 @@
{
struct proc_dir_entry *ent;
- ent = __proc_create(&parent, name, S_IFDIR | S_IRUGO | S_IXUGO, 2);
+ ent = __proc_create(&parent, name, S_IFDIR | S_IRUGO | S_IXUGO, 2, IATTR_ADMIN);
if (ent) {
ent->data = net;
if (proc_register(parent, ent) < 0) {
@@ -727,7 +729,7 @@
nlink = 1;
}
- ent = __proc_create(&parent, name, mode, nlink);
+ ent = __proc_create(&parent, name, mode, nlink, IATTR_PROC_DEFAULT);
if (ent) {
if (proc_register(parent, ent) < 0) {
kfree(ent);
@@ -745,6 +747,8 @@
{
struct proc_dir_entry *pde;
nlink_t nlink;
+ int do_check_netns = 0;
+ int vx_flags = IATTR_PROC_DEFAULT;
if (S_ISDIR(mode)) {
if ((mode & S_IALLUGO) == 0)
@@ -758,7 +762,33 @@
nlink = 1;
}
- pde = __proc_create(&parent, name, mode, nlink);
+ // If we're creating an entry below /proc/.../net and we're not the
+ // initial network namespace, use IATTR_ADMIN instead of IATTR_PROC_DEFAULT
+ // (that includes IATTR_HIDE, which causes the whole problem of /proc/net
+ // not being shown on vservers with network namespaces).
+ // This is most certainly a big fat ugly kludge... perhaps we should make
+ // vx_check know about net namespaces or change the pernet_operations
+ // know about vservers (this would require chances in a lot of places,
+ // I guess). This is a quick fix I came up with without delving too deep
+ // or risk changing too many things. Anyway, as far as I can see, it solves
+ // the problem without creating creating any new security holes.
+
+ if (parent && parent->namelen == 3) {
+ if (!memcmp(parent->name,"net",3) && parent->parent) {
+ if ( parent->parent->namelen == 2 ) {
+ if (memcmp(parent->parent->name,"ns",2)) {
+ do_check_netns = 1;
+ }
+ } else {
+ do_check_netns = 1;
+ }
+ }
+ if (do_check_netns) {
+ if (parent->data != (void *)&init_net) vx_flags = IATTR_ADMIN;
+ }
+ }
+
+ pde = __proc_create(&parent, name, mode, nlink, vx_flags);
if (!pde)
goto out;
pde->proc_fops = proc_fops;