[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20250318083422.21489-5-cgoettsche@seltendoof.de>
Date: Tue, 18 Mar 2025 09:33:33 +0100
From: Christian Göttsche <cgoettsche@...tendoof.de>
To:
Cc: Christian Göttsche <cgzones@...glemail.com>,
Paul Moore <paul@...l-moore.com>,
Stephen Smalley <stephen.smalley.work@...il.com>,
Ondrej Mosnacek <omosnace@...hat.com>,
Thiébaud Weksteen <tweek@...gle.com>,
Bram Bonné <brambonne@...gle.com>,
Casey Schaufler <casey@...aufler-ca.com>,
Canfeng Guo <guocanfeng@...ontech.com>,
GUO Zihua <guozihua@...wei.com>,
selinux@...r.kernel.org,
linux-kernel@...r.kernel.org
Subject: [RFC PATCH 6/6] selinux: add cache stats for network tables
From: Christian Göttsche <cgzones@...glemail.com>
Export utilization statistics for network object labeling related hash
tables, similar to AVC and SID hash tables, to userspace via new
selinuxfs files under /stats/.
Guard this functionality with a new compile time option
SECURITY_SELINUX_NETTABLE_STATS.
Signed-off-by: Christian Göttsche <cgzones@...glemail.com>
---
security/selinux/Kconfig | 8 ++
security/selinux/ibpkey.c | 33 +++++++
security/selinux/include/ibpkey.h | 5 +
security/selinux/include/netif.h | 4 +
security/selinux/include/netnode.h | 4 +
security/selinux/include/netport.h | 4 +
security/selinux/netif.c | 39 ++++++++
security/selinux/netnode.c | 33 +++++++
security/selinux/netport.c | 33 +++++++
security/selinux/selinuxfs.c | 152 ++++++++++++++++++++++++++++-
10 files changed, 313 insertions(+), 2 deletions(-)
diff --git a/security/selinux/Kconfig b/security/selinux/Kconfig
index 61abc1e094a8..cae0c7b2c994 100644
--- a/security/selinux/Kconfig
+++ b/security/selinux/Kconfig
@@ -46,6 +46,14 @@ config SECURITY_SELINUX_AVC_STATS
/sys/fs/selinux/avc/cache_stats, which may be monitored via
tools such as avcstat.
+config SECURITY_SELINUX_NETTABLE_STATS
+ bool "SELinux Network Hashtable Statistics"
+ depends on SECURITY_SELINUX
+ default y
+ help
+ This option collects network hash table statistics to
+ /sys/fs/selinux/stats/.
+
config SECURITY_SELINUX_SIDTAB_HASH_BITS
int "SELinux sidtab hashtable size"
depends on SECURITY_SELINUX
diff --git a/security/selinux/ibpkey.c b/security/selinux/ibpkey.c
index 470481cfe0e8..c1ad58297ac4 100644
--- a/security/selinux/ibpkey.c
+++ b/security/selinux/ibpkey.c
@@ -218,6 +218,39 @@ void sel_ib_pkey_flush(void)
spin_unlock_irqrestore(&sel_ib_pkey_lock, flags);
}
+#ifdef CONFIG_SECURITY_SELINUX_NETTABLE_STATS
+/**
+ * sel_ib_pkey_get_hash_stats - Dump pkey table statistics
+ * @page: the page sized buffer to write to
+ *
+ * Description:
+ * Make the utilization of the hash table available for userspace for
+ * introspection.
+ *
+ */
+int sel_ib_pkey_get_hash_stats(char *page)
+{
+ unsigned int idx, chain_len, max_chain_len = 0, slots_used = 0, total = 0;
+ unsigned long long chain2_len_sum = 0;
+
+ for (idx = 0; idx < SEL_PKEY_HASH_SIZE; idx++) {
+ chain_len = sel_ib_pkey_hash[idx].size;
+
+ if (chain_len > 0)
+ slots_used++;
+ if (chain_len > max_chain_len)
+ max_chain_len = chain_len;
+ total += chain_len;
+ chain2_len_sum += (unsigned long long)chain_len * chain_len;
+ }
+
+ return scnprintf(page, PAGE_SIZE, "entries: %d\nbuckets used: %d/%d\n"
+ "longest chain: %d\nsum of chain length^2: %llu\n",
+ total, slots_used, SEL_PKEY_HASH_SIZE, max_chain_len,
+ chain2_len_sum);
+}
+#endif /* CONFIG_SECURITY_SELINUX_NETTABLE_STATS */
+
static __init int sel_ib_pkey_init(void)
{
int iter;
diff --git a/security/selinux/include/ibpkey.h b/security/selinux/include/ibpkey.h
index 875b055849e1..2d84877fc8c5 100644
--- a/security/selinux/include/ibpkey.h
+++ b/security/selinux/include/ibpkey.h
@@ -20,6 +20,11 @@
#ifdef CONFIG_SECURITY_INFINIBAND
void sel_ib_pkey_flush(void);
int sel_ib_pkey_sid(u64 subnet_prefix, u16 pkey, u32 *sid);
+
+#ifdef CONFIG_SECURITY_SELINUX_NETTABLE_STATS
+int sel_ib_pkey_get_hash_stats(char *page);
+#endif
+
#else
static inline void sel_ib_pkey_flush(void)
{
diff --git a/security/selinux/include/netif.h b/security/selinux/include/netif.h
index 2838bdc170dd..7246eb3ebc71 100644
--- a/security/selinux/include/netif.h
+++ b/security/selinux/include/netif.h
@@ -21,4 +21,8 @@ void sel_netif_flush(void);
int sel_netif_sid(struct net *ns, int ifindex, u32 *sid);
+#ifdef CONFIG_SECURITY_SELINUX_NETTABLE_STATS
+int sel_netif_get_hash_stats(char *page);
+#endif
+
#endif /* _SELINUX_NETIF_H_ */
diff --git a/security/selinux/include/netnode.h b/security/selinux/include/netnode.h
index e4dc904c3585..897c72b4b664 100644
--- a/security/selinux/include/netnode.h
+++ b/security/selinux/include/netnode.h
@@ -23,4 +23,8 @@ void sel_netnode_flush(void);
int sel_netnode_sid(const void *addr, u16 family, u32 *sid);
+#ifdef CONFIG_SECURITY_SELINUX_NETTABLE_STATS
+int sel_netnode_get_hash_stats(char *page);
+#endif
+
#endif
diff --git a/security/selinux/include/netport.h b/security/selinux/include/netport.h
index 9096a8289948..1b9744656616 100644
--- a/security/selinux/include/netport.h
+++ b/security/selinux/include/netport.h
@@ -22,4 +22,8 @@ void sel_netport_flush(void);
int sel_netport_sid(u8 protocol, u16 pnum, u32 *sid);
+#ifdef CONFIG_SECURITY_SELINUX_NETTABLE_STATS
+int sel_netport_get_hash_stats(char *page);
+#endif
+
#endif
diff --git a/security/selinux/netif.c b/security/selinux/netif.c
index 2ab7fe9e1ea2..f7bdf75f871e 100644
--- a/security/selinux/netif.c
+++ b/security/selinux/netif.c
@@ -250,6 +250,45 @@ void sel_netif_flush(void)
spin_unlock_bh(&sel_netif_lock);
}
+#ifdef CONFIG_SECURITY_SELINUX_NETTABLE_STATS
+/**
+ * sel_netif_get_hash_stats - Dump network interface table statistics
+ * @page: the page sized buffer to write to
+ *
+ * Description:
+ * Make the utilization of the hash table available for userspace for
+ * introspection.
+ *
+ */
+int sel_netif_get_hash_stats(char *page)
+{
+ unsigned int idx, chain_len, max_chain_len = 0, slots_used = 0, total = 0;
+ unsigned long long chain2_len_sum = 0;
+ const struct sel_netif *netif;
+
+ rcu_read_lock();
+ for (idx = 0; idx < SEL_NETIF_HASH_SIZE; idx++) {
+ chain_len = 0;
+
+ list_for_each_entry_rcu(netif, &sel_netif_hash[idx], list)
+ chain_len++;
+
+ if (chain_len > 0)
+ slots_used++;
+ if (chain_len > max_chain_len)
+ max_chain_len = chain_len;
+ total += chain_len;
+ chain2_len_sum += (unsigned long long)chain_len * chain_len;
+ }
+ rcu_read_unlock();
+
+ return scnprintf(page, PAGE_SIZE, "entries: %d/%d\nbuckets used: %d/%d\n"
+ "longest chain: %d\nsum of chain length^2: %llu\n",
+ total, SEL_NETIF_HASH_MAX, slots_used, SEL_NETIF_HASH_SIZE,
+ max_chain_len, chain2_len_sum);
+}
+#endif /* CONFIG_SECURITY_SELINUX_NETTABLE_STATS */
+
static int sel_netif_netdev_notifier_handler(struct notifier_block *this,
unsigned long event, void *ptr)
{
diff --git a/security/selinux/netnode.c b/security/selinux/netnode.c
index 15fdf385062e..0e380ee82eb2 100644
--- a/security/selinux/netnode.c
+++ b/security/selinux/netnode.c
@@ -290,6 +290,39 @@ void sel_netnode_flush(void)
spin_unlock_bh(&sel_netnode_lock);
}
+#ifdef CONFIG_SECURITY_SELINUX_NETTABLE_STATS
+/**
+ * sel_netnode_get_hash_stats - Dump network address table statistics
+ * @page: the page sized buffer to write to
+ *
+ * Description:
+ * Make the utilization of the hash table available for userspace for
+ * introspection.
+ *
+ */
+int sel_netnode_get_hash_stats(char *page)
+{
+ unsigned int idx, chain_len, max_chain_len = 0, slots_used = 0, total = 0;
+ unsigned long long chain2_len_sum = 0;
+
+ for (idx = 0; idx < SEL_NETNODE_HASH_SIZE; idx++) {
+ chain_len = sel_netnode_hash[idx].size;
+
+ if (chain_len > 0)
+ slots_used++;
+ if (chain_len > max_chain_len)
+ max_chain_len = chain_len;
+ total += chain_len;
+ chain2_len_sum += (unsigned long long)chain_len * chain_len;
+ }
+
+ return scnprintf(page, PAGE_SIZE, "entries: %d\nbuckets used: %d/%d\n"
+ "longest chain: %d\nsum of chain length^2: %llu\n",
+ total, slots_used, SEL_NETNODE_HASH_SIZE, max_chain_len,
+ chain2_len_sum);
+}
+#endif /* CONFIG_SECURITY_SELINUX_NETTABLE_STATS */
+
static __init int sel_netnode_init(void)
{
int iter;
diff --git a/security/selinux/netport.c b/security/selinux/netport.c
index 648c2bce83a7..2a315dcc4344 100644
--- a/security/selinux/netport.c
+++ b/security/selinux/netport.c
@@ -224,6 +224,39 @@ void sel_netport_flush(void)
spin_unlock_bh(&sel_netport_lock);
}
+#ifdef CONFIG_SECURITY_SELINUX_NETTABLE_STATS
+/**
+ * sel_netport_get_hash_stats - Dump network port table statistics
+ * @page: the page sized buffer to write to
+ *
+ * Description:
+ * Make the utilization of the hash table available for userspace for
+ * introspection.
+ *
+ */
+int sel_netport_get_hash_stats(char *page)
+{
+ unsigned int idx, chain_len, max_chain_len = 0, slots_used = 0, total = 0;
+ unsigned long long chain2_len_sum = 0;
+
+ for (idx = 0; idx < SEL_NETPORT_HASH_SIZE; idx++) {
+ chain_len = sel_netport_hash[idx].size;
+
+ if (chain_len > 0)
+ slots_used++;
+ if (chain_len > max_chain_len)
+ max_chain_len = chain_len;
+ total += chain_len;
+ chain2_len_sum += (unsigned long long)chain_len * chain_len;
+ }
+
+ return scnprintf(page, PAGE_SIZE, "entries: %d\nbuckets used: %d/%d\n"
+ "longest chain: %d\nsum of chain length^2: %llu\n",
+ total, slots_used, SEL_NETPORT_HASH_SIZE, max_chain_len,
+ chain2_len_sum);
+}
+#endif /* CONFIG_SECURITY_SELINUX_NETTABLE_STATS */
+
static __init int sel_netport_init(void)
{
int iter;
diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
index 47480eb2189b..815c509a633b 100644
--- a/security/selinux/selinuxfs.c
+++ b/security/selinux/selinuxfs.c
@@ -42,6 +42,10 @@
#include "objsec.h"
#include "conditional.h"
#include "ima.h"
+#include "ibpkey.h"
+#include "netif.h"
+#include "netnode.h"
+#include "netport.h"
enum sel_inos {
SEL_ROOT_INO = 2,
@@ -1619,6 +1623,138 @@ static int sel_make_avc_files(struct dentry *dir)
return 0;
}
+#ifdef CONFIG_SECURITY_SELINUX_NETTABLE_STATS
+static ssize_t sel_read_netif_stats(struct file *filp, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char *page;
+ ssize_t length;
+
+ page = (char *)__get_free_page(GFP_KERNEL);
+ if (!page)
+ return -ENOMEM;
+
+ length = sel_netif_get_hash_stats(page);
+ if (length >= 0)
+ length = simple_read_from_buffer(buf, count, ppos, page, length);
+ free_page((unsigned long)page);
+
+ return length;
+}
+
+static const struct file_operations sel_netif_stats_ops = {
+ .read = sel_read_netif_stats,
+ .llseek = generic_file_llseek,
+};
+
+static ssize_t sel_read_netnode_stats(struct file *filp, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char *page;
+ ssize_t length;
+
+ page = (char *)__get_free_page(GFP_KERNEL);
+ if (!page)
+ return -ENOMEM;
+
+ length = sel_netnode_get_hash_stats(page);
+ if (length >= 0)
+ length = simple_read_from_buffer(buf, count, ppos, page, length);
+ free_page((unsigned long)page);
+
+ return length;
+}
+
+static const struct file_operations sel_netnode_stats_ops = {
+ .read = sel_read_netnode_stats,
+ .llseek = generic_file_llseek,
+};
+
+static ssize_t sel_read_netport_stats(struct file *filp, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char *page;
+ ssize_t length;
+
+ page = (char *)__get_free_page(GFP_KERNEL);
+ if (!page)
+ return -ENOMEM;
+
+ length = sel_netport_get_hash_stats(page);
+ if (length >= 0)
+ length = simple_read_from_buffer(buf, count, ppos, page, length);
+ free_page((unsigned long)page);
+
+ return length;
+}
+
+static const struct file_operations sel_netport_stats_ops = {
+ .read = sel_read_netport_stats,
+ .llseek = generic_file_llseek,
+};
+
+#ifdef CONFIG_SECURITY_INFINIBAND
+static ssize_t sel_read_ib_pkey_stats(struct file *filp, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char *page;
+ ssize_t length;
+
+ page = (char *)__get_free_page(GFP_KERNEL);
+ if (!page)
+ return -ENOMEM;
+
+ length = sel_ib_pkey_get_hash_stats(page);
+ if (length >= 0)
+ length = simple_read_from_buffer(buf, count, ppos, page, length);
+ free_page((unsigned long)page);
+
+ return length;
+}
+
+static const struct file_operations sel_ib_pkey_stats_ops = {
+ .read = sel_read_ib_pkey_stats,
+ .llseek = generic_file_llseek,
+};
+#endif /* CONFIG_SECURITY_INFINIBAND */
+
+static int sel_make_stats_files(struct dentry *dir)
+{
+ struct super_block *sb = dir->d_sb;
+ struct selinux_fs_info *fsi = sb->s_fs_info;
+ unsigned int i;
+ static const struct tree_descr files[] = {
+ { "netif_hash_stats", &sel_netif_stats_ops, 0444 },
+ { "netnode_hash_stats", &sel_netnode_stats_ops, 0444 },
+ { "netport_hash_stats", &sel_netport_stats_ops, 0444 },
+#ifdef CONFIG_SECURITY_INFINIBAND
+ { "ibpkey_hash_stats", &sel_ib_pkey_stats_ops, 0444 },
+#endif
+ };
+
+ for (i = 0; i < ARRAY_SIZE(files); i++) {
+ struct inode *inode;
+ struct dentry *dentry;
+
+ dentry = d_alloc_name(dir, files[i].name);
+ if (!dentry)
+ return -ENOMEM;
+
+ inode = sel_make_inode(dir->d_sb, S_IFREG|files[i].mode);
+ if (!inode) {
+ dput(dentry);
+ return -ENOMEM;
+ }
+
+ inode->i_fop = files[i].ops;
+ inode->i_ino = ++fsi->last_ino;
+ d_add(dentry, inode);
+ }
+
+ return 0;
+}
+#endif /* CONFIG_SECURITY_SELINUX_NETTABLE_STATS */
+
static int sel_make_ss_files(struct dentry *dir)
{
struct super_block *sb = dir->d_sb;
@@ -2051,6 +2187,18 @@ static int sel_fill_super(struct super_block *sb, struct fs_context *fc)
if (ret)
goto err;
+#ifdef CONFIG_SECURITY_SELINUX_NETTABLE_STATS
+ dentry = sel_make_dir(sb->s_root, "stats", &fsi->last_ino);
+ if (IS_ERR(dentry)) {
+ ret = PTR_ERR(dentry);
+ goto err;
+ }
+
+ ret = sel_make_stats_files(dentry);
+ if (ret)
+ goto err;
+#endif /* CONFIG_SECURITY_SELINUX_NETTABLE_STATS */
+
dentry = sel_make_dir(sb->s_root, "ss", &fsi->last_ino);
if (IS_ERR(dentry)) {
ret = PTR_ERR(dentry);
@@ -2094,8 +2242,8 @@ static int sel_fill_super(struct super_block *sb, struct fs_context *fc)
return 0;
err:
- pr_err("SELinux: %s: failed while creating inodes\n",
- __func__);
+ pr_err("SELinux: %s: failed while creating inodes: %d\n",
+ __func__, ret);
selinux_fs_info_free(sb);
--
2.49.0
Powered by blists - more mailing lists