[PATCH 2/3] lib/kobject_uevent.c: add uevent forwarding function

From: Michael J. Coss
Date: Tue Sep 08 2015 - 22:23:13 EST


Adds capability to allow userspace programs to forward a given event to
a specific network namespace as determined by the provided pid. In
addition, support for a per-namespace kobject_sequence counter was
added. Sysfs was modified to return the correct event counter based on
the current network namespace.

Signed-off-by: Michael J. Coss <michael.coss@xxxxxxxxxxxxxxxxxx>
---
include/linux/kobject.h | 3 ++
include/net/net_namespace.h | 3 ++
kernel/ksysfs.c | 12 ++++++
lib/kobject_uevent.c | 90 +++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 108 insertions(+)

diff --git a/include/linux/kobject.h b/include/linux/kobject.h
index 637f670..d1bb509 100644
--- a/include/linux/kobject.h
+++ b/include/linux/kobject.h
@@ -215,6 +215,9 @@ extern struct kobject *firmware_kobj;
int kobject_uevent(struct kobject *kobj, enum kobject_action action);
int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
char *envp[]);
+#if defined(CONFIG_UDEVNS) || defined(CONFIG_UDEVNS_MODULE)
+int kobject_uevent_forward(char *buf, size_t len, pid_t pid);
+#endif

__printf(2, 3)
int add_uevent_var(struct kobj_uevent_env *env, const char *format, ...);
diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h
index e951453..a4013e5 100644
--- a/include/net/net_namespace.h
+++ b/include/net/net_namespace.h
@@ -134,6 +134,9 @@ struct net {
#if IS_ENABLED(CONFIG_MPLS)
struct netns_mpls mpls;
#endif
+#if defined(CONFIG_UDEVNS) || defined(CONFIG_UDEVNS_MODULE)
+ u64 kevent_seqnum;
+#endif
struct sock *diag_nlsk;
atomic_t fnhe_genid;
};
diff --git a/kernel/ksysfs.c b/kernel/ksysfs.c
index 6683cce..4bc15fd 100644
--- a/kernel/ksysfs.c
+++ b/kernel/ksysfs.c
@@ -21,6 +21,9 @@
#include <linux/compiler.h>

#include <linux/rcupdate.h> /* rcu_expedited */
+#if defined(CONFIG_UDEVNS) || defined(CONFIG_UDEVNS_MODULE)
+#include <net/net_namespace.h>
+#endif

#define KERNEL_ATTR_RO(_name) \
static struct kobj_attribute _name##_attr = __ATTR_RO(_name)
@@ -33,6 +36,15 @@ static struct kobj_attribute _name##_attr = \
static ssize_t uevent_seqnum_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
+#if defined(CONFIG_UDEVNS) || defined(CONFIG_UDEVNS_MODULE)
+ pid_t p = task_pid_vnr(current);
+ struct net *n = get_net_ns_by_pid(p);
+
+ if (n != ERR_PTR(-ESRCH)) {
+ if (!net_eq(n, &init_net))
+ return sprintf(buf, "%llu\n", n->kevent_seqnum);
+ }
+#endif
return sprintf(buf, "%llu\n", (unsigned long long)uevent_seqnum);
}
KERNEL_ATTR_RO(uevent_seqnum);
diff --git a/lib/kobject_uevent.c b/lib/kobject_uevent.c
index d791e33..7589745 100644
--- a/lib/kobject_uevent.c
+++ b/lib/kobject_uevent.c
@@ -379,6 +379,96 @@ int kobject_uevent(struct kobject *kobj, enum kobject_action action)
}
EXPORT_SYMBOL_GPL(kobject_uevent);

+#if defined(CONFIG_UDEVNS) || defined(CONFIG_UDEVNS_MODULE)
+/**
+ * kobject_uevent_forward - forward event to specified network namespace
+ *
+ * @buf: event buffer
+ * @len: event length
+ * @pid: pid of network namespace
+ *
+ * Returns 0 if kobject_uevent_forward() is completed with success or the
+ * corresponding error when it fails.
+ */
+int kobject_uevent_forward(char *buf, size_t len, pid_t pid)
+{
+ int retval = 0;
+#if defined(CONFIG_NET)
+ struct uevent_sock *ue_sk;
+ struct net *pns;
+ char *p;
+ u64 num;
+
+ /* grab the network namespace of the provided pid */
+ pns = get_net_ns_by_pid(pid);
+ if (pns == ERR_PTR(-ESRCH))
+ return -ESRCH;
+
+ /* find sequence number in buffer */
+ p = buf;
+ num = 0;
+ while (p < (buf + len)) {
+ if (strncmp(p, "SEQNUM=", 7) == 0) {
+ int r;
+
+ p += 7;
+ r = kstrtoull(p, 10, &num);
+ if (r) {
+ put_net(pns);
+ return r;
+ }
+ break;
+ }
+ p += (strlen(p) + 1);
+ }
+
+ /* if we didn't see a valid seqnum, or none was present, return error */
+ if (num == 0) {
+ put_net(pns);
+ return -EINVAL;
+ }
+ /* update per namespace sequence number as needed */
+ if (pns->kevent_seqnum < num)
+ pns->kevent_seqnum = num;
+
+ list_for_each_entry(ue_sk, &uevent_sock_list, list) {
+ struct sock *uevent_sock = ue_sk->sk;
+ struct sk_buff *skb;
+
+ if (!netlink_has_listeners(uevent_sock, 1))
+ continue;
+ /*
+ * only send to sockets share the same network namespace
+ * as the passed pid
+ */
+ if (!net_eq(sock_net(uevent_sock), pns))
+ continue;
+
+ /* allocate message with the maximum possible size */
+ skb = alloc_skb(len, GFP_KERNEL);
+ if (skb) {
+ char *p;
+
+ p = skb_put(skb, len);
+ memcpy(p, buf, len);
+ NETLINK_CB(skb).dst_group = 1;
+ retval = netlink_broadcast(uevent_sock, skb, 0, 1,
+ GFP_KERNEL);
+
+ /* ENOBUFS should be handled in userspace */
+ if (retval == -ENOBUFS || retval == -ESRCH)
+ retval = 0;
+ } else {
+ retval = -ENOMEM;
+ }
+ }
+ put_net(pns);
+#endif
+ return retval;
+}
+EXPORT_SYMBOL_GPL(kobject_uevent_forward);
+#endif
+
/**
* add_uevent_var - add key value string to the environment buffer
* @env: environment buffer structure
--
2.4.6

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/