Subject: Kludgy fix to missing /proc/net under net namespaces
From: Marco Carnut <kiko@tempest.com.br>
Date: Wed, 01 May 2013 21:38:02 -0300
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;