[Devel] [PATCH 1/6] proc connector: per-net operations introduced
Stanislav Kinsburskiy
skinsbursky at virtuozzo.com
Tue Aug 15 15:41:54 MSK 2017
The idea of this patch is to containerize all the static objects, used by proc
connector, on per-net basis.
There are not tha many: 4 objects, defined in per-net "cn_net" structure.
And this structure is allocated and initialized only for initial network
namespace in initial user namespace.
Signed-off-by: Stanislav Kinsburskiy <skinsbursky at virtuozzo.com>
---
drivers/connector/cn_proc.c | 35 +++++++++++++
drivers/connector/connector.c | 114 +++++++++++++++++++++++++++++++++++++++++
drivers/connector/netns.h | 22 ++++++++
3 files changed, 171 insertions(+)
create mode 100644 drivers/connector/netns.h
diff --git a/drivers/connector/cn_proc.c b/drivers/connector/cn_proc.c
index 3165811..dcd993e 100644
--- a/drivers/connector/cn_proc.c
+++ b/drivers/connector/cn_proc.c
@@ -34,6 +34,8 @@
#include <linux/cn_proc.h>
+#include "netns.h"
+
/*
* Size of a cn_msg followed by a proc_event structure. Since the
* sizeof struct cn_msg is a multiple of 4 bytes, but not 8 bytes, we
@@ -391,6 +393,39 @@ static void cn_proc_mcast_ctl(struct cn_msg *msg,
cn_proc_ack(err, msg->seq, msg->ack);
}
+int __net_init cn_proc_init_net(struct net *net)
+{
+ struct cn_net *cnn = net_generic(net, cn_net_id);
+ struct cn_dev *dev = cnn->cdev;
+ int err;
+
+ cnn->proc_event_counts = alloc_percpu(u32);
+ if (!cnn->proc_event_counts)
+ return -ENOMEM;
+
+ err = cn_queue_add_callback(dev->cbdev, "cn_proc",
+ &cn_proc_event_id, cn_proc_mcast_ctl);
+ if (err) {
+ pr_warn("cn_proc failed to register\n");
+ free_percpu(cnn->proc_event_counts);
+ return err;
+ }
+
+ atomic_set(&cnn->proc_event_num_listeners, 0);
+
+ return 0;
+}
+
+void __net_exit cn_proc_exit_net(struct net *net)
+{
+ struct cn_net *cnn = net_generic(net, cn_net_id);
+ struct cn_dev *dev = cnn->cdev;
+
+ cn_queue_del_callback(dev->cbdev, &cn_proc_event_id);
+
+ free_percpu(cnn->proc_event_counts);
+}
+
/*
* cn_proc_init - initialization entry point
*
diff --git a/drivers/connector/connector.c b/drivers/connector/connector.c
index 0daa11e..d0b678f 100644
--- a/drivers/connector/connector.c
+++ b/drivers/connector/connector.c
@@ -33,6 +33,8 @@
#include <net/sock.h>
+#include "netns.h"
+
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Evgeniy Polyakov <zbr at ioremap.net>");
MODULE_DESCRIPTION("Generic userspace <-> kernelspace connector.");
@@ -253,6 +255,116 @@ static const struct file_operations cn_file_ops = {
.release = single_release
};
+static struct cn_dev *cn_init_dev_net(struct net *net)
+{
+ struct cn_dev *dev;
+ struct netlink_kernel_cfg cfg = {
+ .groups = CN_NETLINK_USERS + 0xf,
+ .input = cn_rx_skb,
+ };
+ int err;
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return ERR_PTR(-ENOMEM);
+
+ dev->nls = netlink_kernel_create(net, NETLINK_CONNECTOR, &cfg);
+ if (!dev->nls) {
+ err = -EIO;
+ goto free_dev;
+ }
+
+ dev->cbdev = cn_queue_alloc_dev("cqueue", dev->nls);
+ if (!dev->cbdev) {
+ err = -EINVAL;
+ goto nl_release;
+ }
+
+ if (!proc_net_create("connector", S_IRUGO, net->proc_net, &cn_file_ops)) {
+ err = -ENOMEM;
+ goto free_cbdev;
+ }
+
+ return dev;
+
+free_cbdev:
+ cn_queue_free_dev(dev->cbdev);
+nl_release:
+ netlink_kernel_release(dev->nls);
+free_dev:
+ kfree(dev);
+ return ERR_PTR(err);
+}
+
+static void cn_fini_dev_net(struct net *net)
+{
+ struct cn_net *cnn = net_generic(net, cn_net_id);
+ struct cn_dev *dev = cnn->cdev;
+
+ remove_proc_entry("connector", net->proc_net);
+
+ cn_queue_free_dev(dev->cbdev);
+ netlink_kernel_release(dev->nls);
+ kfree(dev);
+}
+
+int cn_net_id;
+
+static bool cn_allowed(struct net *net)
+{
+ /* Create device only for containers init user ns */
+ if (!current_user_ns_initial())
+ return false;
+
+ /* Create device only for containers init net */
+ if (net->owner_ve->ve_netns != net)
+ return false;
+
+ return true;
+}
+
+static int __net_init cn_init_net(struct net *net)
+{
+ struct cn_net *cnn = net_generic(net, cn_net_id);
+ int err;
+
+ if (!cn_allowed(net))
+ return 0;
+
+ cnn->cdev = cn_init_dev_net(net);
+ if (IS_ERR(cnn->cdev))
+ return PTR_ERR(cnn->cdev);
+
+ err = cn_proc_init_net(net);
+ if (err) {
+ cn_fini_dev_net(net);
+ return err;
+ }
+
+ cnn->cn_already_initialized = 1;
+ return 0;
+}
+
+static void __net_exit cn_exit_net(struct net *net)
+{
+ struct cn_net *cnn = net_generic(net, cn_net_id);
+
+ if (!cn_allowed(net))
+ return;
+
+ cnn->cn_already_initialized = 0;
+
+ cn_proc_exit_net(net);
+ cn_fini_dev_net(net);
+}
+
+static struct pernet_operations cn_pernet_ops = {
+ .init = cn_init_net,
+ .exit = cn_exit_net,
+ .id = &cn_net_id,
+ .size = sizeof(struct cn_net),
+};
+
static struct cn_dev cdev = {
.input = cn_rx_skb,
};
@@ -265,6 +377,8 @@ static int cn_init(void)
.input = dev->input,
};
+ (void) cn_pernet_ops;
+
dev->nls = netlink_kernel_create(&init_net, NETLINK_CONNECTOR, &cfg);
if (!dev->nls)
return -EIO;
diff --git a/drivers/connector/netns.h b/drivers/connector/netns.h
new file mode 100644
index 0000000..28387f6
--- /dev/null
+++ b/drivers/connector/netns.h
@@ -0,0 +1,22 @@
+#ifndef __CONNECTOR_NETNS__
+#define __CONNECTOR_NETNS__
+
+#include <net/netns/generic.h>
+
+extern int cn_net_id;
+
+struct cn_dev;
+struct cn_net {
+ struct cn_dev *cdev;
+ int cn_already_initialized;
+
+ atomic_t proc_event_num_listeners;
+ u32 __percpu *proc_event_counts;
+
+};
+
+struct net;
+int cn_proc_init_net(struct net *net);
+void cn_proc_exit_net(struct net *net);
+
+#endif // __CONNECTOR_NETNS__
More information about the Devel
mailing list