[v2,RH7] kernfs/sysfs: add ioctl to get fd network namespace tag

Submitted by Pavel Tikhomirov on July 20, 2020, 11:19 a.m.

Details

Message ID 20200720111959.521226-1-ptikhomirov@virtuozzo.com
State New
Series "kernfs/sysfs: add ioctl to get fd network namespace tag"
Headers show

Commit Message

Pavel Tikhomirov July 20, 2020, 11:19 a.m.
Sysfs mounts save current netns when mounting and show different set of
network devices in /sys/class/net based on it. But there is currently no
simple way to find out to that netns the sysfs mount is tagged to.

Lets add an ioctl so that we can get open file descriptor to this
namespace on nsfs. Next we would be able to readlink this fd to get
network namespace id.

Sysfs holds only "passive" reference to the net namespace, so the only
guaranty we have is that struct net is not freed yet. So lets add a
helper maybe_get_net_ns to get net only if it's fully alive.

https://jira.sw.ru/browse/PSBM-105161

v2: fix bad net namespace referencing

Signed-off-by: Pavel Tikhomirov <ptikhomirov@virtuozzo.com>
---
 fs/kernfs/dir.c             |  1 +
 fs/kernfs/file.c            | 23 +++++++++++++++++++++++
 fs/kernfs/kernfs-internal.h |  1 +
 include/linux/kernfs.h      |  5 +++++
 include/linux/socket.h      |  1 +
 net/socket.c                | 12 ++++++++++++
 6 files changed, 43 insertions(+)

Patch hide | download patch | download mbox

diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c
index 522947230f65..01d2e53acef2 100644
--- a/fs/kernfs/dir.c
+++ b/fs/kernfs/dir.c
@@ -1469,4 +1469,5 @@  const struct file_operations kernfs_dir_fops = {
 	.iterate	= kernfs_fop_readdir,
 	.release	= kernfs_dir_fop_release,
 	.llseek		= kernfs_dir_fop_llseek,
+	.unlocked_ioctl = kernfs_ioctl,
 };
diff --git a/fs/kernfs/file.c b/fs/kernfs/file.c
index 3886845a2678..963a5d05d4f3 100644
--- a/fs/kernfs/file.c
+++ b/fs/kernfs/file.c
@@ -15,6 +15,10 @@ 
 #include <linux/pagemap.h>
 #include <linux/sched.h>
 #include <linux/fsnotify.h>
+#include <linux/proc_ns.h>
+#include <linux/magic.h>
+#include <linux/socket.h>
+#include <net/net_namespace.h>
 
 #include "kernfs-internal.h"
 
@@ -891,6 +895,24 @@  void kernfs_notify(struct kernfs_node *kn)
 }
 EXPORT_SYMBOL_GPL(kernfs_notify);
 
+long kernfs_ioctl(struct file *file, unsigned int ioctl,
+		  unsigned long arg)
+{
+	struct dentry *dentry = file->f_path.dentry;
+	const void *ns = kernfs_info(dentry->d_sb)->ns;
+	struct net *net;
+
+	switch (ioctl) {
+	case KERNFS_GET_NS:
+		if (dentry->d_sb->s_magic != SYSFS_MAGIC || !ns)
+			return -ENOTTY;
+		net = (struct net *)ns;
+		return open_related_ns(&net->ns, maybe_get_net_ns);
+	default:
+		return -ENOTTY;
+	}
+}
+
 const struct file_operations kernfs_file_fops = {
 	.read		= kernfs_fop_read,
 	.write		= kernfs_fop_write,
@@ -899,6 +921,7 @@  const struct file_operations kernfs_file_fops = {
 	.open		= kernfs_fop_open,
 	.release	= kernfs_fop_release,
 	.poll		= kernfs_fop_poll,
+	.unlocked_ioctl = kernfs_ioctl,
 };
 
 /**
diff --git a/fs/kernfs/kernfs-internal.h b/fs/kernfs/kernfs-internal.h
index 0dab7ce93f22..fa102d625a49 100644
--- a/fs/kernfs/kernfs-internal.h
+++ b/fs/kernfs/kernfs-internal.h
@@ -119,6 +119,7 @@  struct kernfs_node *kernfs_new_node(struct kernfs_node *parent,
 extern const struct file_operations kernfs_file_fops;
 
 void kernfs_unmap_bin_file(struct kernfs_node *kn);
+long kernfs_ioctl(struct file *file, unsigned int ioctl, unsigned long arg);
 
 /*
  * symlink.c
diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h
index 485360932341..d3fe1f6dbd4f 100644
--- a/include/linux/kernfs.h
+++ b/include/linux/kernfs.h
@@ -16,6 +16,7 @@ 
 #include <linux/rbtree.h>
 #include <linux/atomic.h>
 #include <linux/wait.h>
+#include <linux/ioctl.h>
 
 struct file;
 struct dentry;
@@ -479,4 +480,8 @@  kernfs_mount(struct file_system_type *fs_type, int flags,
 				magic, new_sb_created, NULL);
 }
 
+#define KERNIO 0xb8
+
+#define KERNFS_GET_NS _IO(KERNIO, 0x1)
+
 #endif	/* __LINUX_KERNFS_H */
diff --git a/include/linux/socket.h b/include/linux/socket.h
index 591cec9a9bd8..790bb8e356b8 100644
--- a/include/linux/socket.h
+++ b/include/linux/socket.h
@@ -356,4 +356,5 @@  extern int __sys_sendmmsg(int fd, struct mmsghdr __user *mmsg,
 			  unsigned int vlen, unsigned int flags);
 
 extern struct ns_common *get_net_ns(struct ns_common *ns);
+extern struct ns_common *maybe_get_net_ns(struct ns_common *ns);
 #endif /* _LINUX_SOCKET_H */
diff --git a/net/socket.c b/net/socket.c
index 3cd3cbd2be1b..a60a04faa6a7 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -1097,6 +1097,18 @@  struct ns_common *get_net_ns(struct ns_common *ns)
 }
 EXPORT_SYMBOL_GPL(get_net_ns);
 
+struct ns_common *maybe_get_net_ns(struct ns_common *ns)
+{
+	struct net *net;
+
+	net = maybe_get_net(container_of(ns, struct net, ns));
+	if (!net)
+		return ERR_PTR(-ENXIO);
+
+	return &net->ns;
+}
+EXPORT_SYMBOL_GPL(maybe_get_net_ns);
+
 static long sock_ioctl(struct file *file, unsigned cmd, unsigned long arg)
 {
 	struct socket *sock;