[dpdk-dev] [RFC] eal: provide option to set vhost_user socket owner/permissions

Christian Ehrhardt christian.ehrhardt at canonical.com
Mon Apr 25 11:18:16 CEST 2016


The API doesn't hold a way to specify a owner/permission set for vhost_user
created sockets. I don't even think an API change would make that much sense.

Projects consuming DPDK start to do 'their own workarounds' like openvswitch
https://patchwork.ozlabs.org/patch/559043/
https://patchwork.ozlabs.org/patch/559045/
But for this specific example they are blocked/stalled behind a bigger
rework (https://patchwork.ozlabs.org/patch/604898/).
Also one could ask why each project would need their own workaround.

At the same time - as I want it for existing code linking against DPDK I
wanted to avoid changing API/ABI. That way I want to provide something existing
users could utilize. So I created a DPDK EAL commandline option based ideas in
the former patches.

For myself I consider this a nice interim solution for existing released
Openvswitch+DPDK solution. And I intend to put it as delta into the DPDK 2.2
currently packaged in Ubuntu to get it working more smoothly with
openvswitch 2.5.

But I'd be interested if DPDK in general would be interested in:
a) an approach like this?
b) would prefer a change of the API?
c) consider it an issue of consuming projects and let them take care?

Signed-off-by: Christian Ehrhardt <christian.ehrhardt at canonical.com>
---
 doc/guides/testpmd_app_ug/run_app.rst        |  19 +++
 lib/librte_eal/common/eal_common_options.c   |   4 +
 lib/librte_eal/common/eal_internal_cfg.h     |   2 +
 lib/librte_eal/common/eal_options.h          |   4 +
 lib/librte_eal/common/include/rte_eal.h      |   5 +
 lib/librte_eal/linuxapp/eal/eal.c            | 182 +++++++++++++++++++++++++++
 lib/librte_vhost/vhost_user/vhost-net-user.c |   4 +
 7 files changed, 220 insertions(+)

diff --git a/doc/guides/testpmd_app_ug/run_app.rst b/doc/guides/testpmd_app_ug/run_app.rst
index f605564..24c9c01 100644
--- a/doc/guides/testpmd_app_ug/run_app.rst
+++ b/doc/guides/testpmd_app_ug/run_app.rst
@@ -156,6 +156,25 @@ See the DPDK Getting Started Guides for more information on these options.
 
     Use malloc instead of hugetlbfs.
 
+*   ``--vhost-owner``
+
+     When creating vhost_user sockets change owner and group to the specified value.
+     This can be given as ``user:group``, but also only ``user`` or ``:group`` are supported.
+
+    Examples::
+
+       --vhost-owner 'libvirt-qemu:kvm'
+       --vhost-owner 'libvirt-qemu'
+       --vhost-owner ':kvm'
+
+*   ``--vhost-perm``
+
+    When creating vhost_user sockets set them up with these permissions.
+
+    For example::
+
+       --vhost-perm '0664'
+
 
 Testpmd Command-line Options
 ----------------------------
diff --git a/lib/librte_eal/common/eal_common_options.c b/lib/librte_eal/common/eal_common_options.c
index 2b418d5..073198b 100644
--- a/lib/librte_eal/common/eal_common_options.c
+++ b/lib/librte_eal/common/eal_common_options.c
@@ -95,6 +95,8 @@ eal_long_options[] = {
 	{OPT_VFIO_INTR,         1, NULL, OPT_VFIO_INTR_NUM        },
 	{OPT_VMWARE_TSC_MAP,    0, NULL, OPT_VMWARE_TSC_MAP_NUM   },
 	{OPT_XEN_DOM0,          0, NULL, OPT_XEN_DOM0_NUM         },
+	{OPT_VHOST_OWNER,       1, NULL, OPT_VHOST_OWNER_NUM      },
+	{OPT_VHOST_PERM,        1, NULL, OPT_VHOST_PERM_NUM       },
 	{0,                     0, NULL, 0                        }
 };
 
@@ -153,6 +155,8 @@ eal_reset_internal_config(struct internal_config *internal_cfg)
 #endif
 	internal_cfg->vmware_tsc_map = 0;
 	internal_cfg->create_uio_dev = 0;
+	internal_cfg->vhost_sock_owner = NULL;
+	internal_cfg->vhost_sock_perm = NULL;
 }
 
 static int
diff --git a/lib/librte_eal/common/eal_internal_cfg.h b/lib/librte_eal/common/eal_internal_cfg.h
index 5f1367e..bdf34e3 100644
--- a/lib/librte_eal/common/eal_internal_cfg.h
+++ b/lib/librte_eal/common/eal_internal_cfg.h
@@ -83,6 +83,8 @@ struct internal_config {
 	volatile enum rte_intr_mode vfio_intr_mode;
 	const char *hugefile_prefix;      /**< the base filename of hugetlbfs files */
 	const char *hugepage_dir;         /**< specific hugetlbfs directory to use */
+	const char *vhost_sock_owner;     /**< owner:group of vhost_user sockets */
+	const char *vhost_sock_perm;      /**< permissions of vhost_user sockets */
 
 	unsigned num_hugepage_sizes;      /**< how many sizes on this system */
 	struct hugepage_info hugepage_info[MAX_HUGEPAGE_SIZES];
diff --git a/lib/librte_eal/common/eal_options.h b/lib/librte_eal/common/eal_options.h
index a881c62..1161083 100644
--- a/lib/librte_eal/common/eal_options.h
+++ b/lib/librte_eal/common/eal_options.h
@@ -83,6 +83,10 @@ enum {
 	OPT_VMWARE_TSC_MAP_NUM,
 #define OPT_XEN_DOM0          "xen-dom0"
 	OPT_XEN_DOM0_NUM,
+#define OPT_VHOST_OWNER       "vhost-owner"
+	OPT_VHOST_OWNER_NUM,
+#define OPT_VHOST_PERM        "vhost-perm"
+	OPT_VHOST_PERM_NUM,
 	OPT_LONG_MAX_NUM
 };
 
diff --git a/lib/librte_eal/common/include/rte_eal.h b/lib/librte_eal/common/include/rte_eal.h
index a71d6f5..506cf24 100644
--- a/lib/librte_eal/common/include/rte_eal.h
+++ b/lib/librte_eal/common/include/rte_eal.h
@@ -252,6 +252,11 @@ static inline int rte_gettid(void)
 	return RTE_PER_LCORE(_thread_id);
 }
 
+/**
+ * Set owner/permissions on sockets if requested on EAL commandline
+ */
+void rte_eal_set_socket_permissions(const char *);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/librte_eal/linuxapp/eal/eal.c b/lib/librte_eal/linuxapp/eal/eal.c
index 8aafd51..3d0b709 100644
--- a/lib/librte_eal/linuxapp/eal/eal.c
+++ b/lib/librte_eal/linuxapp/eal/eal.c
@@ -53,6 +53,9 @@
 #if defined(RTE_ARCH_X86)
 #include <sys/io.h>
 #endif
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
 
 #include <rte_common.h>
 #include <rte_debug.h>
@@ -343,6 +346,8 @@ eal_usage(const char *prgname)
 	       "  --"OPT_CREATE_UIO_DEV"    Create /dev/uioX (usually done by hotplug)\n"
 	       "  --"OPT_VFIO_INTR"         Interrupt mode for VFIO (legacy|msi|msix)\n"
 	       "  --"OPT_XEN_DOM0"          Support running on Xen dom0 without hugetlbfs\n"
+	       "  --"OPT_VHOST_OWNER"       Create vhost-user sockets with this owner:group\n"
+	       "  --"OPT_VHOST_PERM"        Create vhost-user sockets with these permissions\n"
 	       "\n");
 	/* Allow the application to print its usage message too if hook is set */
 	if ( rte_application_usage_hook ) {
@@ -618,6 +623,14 @@ eal_parse_args(int argc, char **argv)
 			internal_config.create_uio_dev = 1;
 			break;
 
+		case OPT_VHOST_OWNER_NUM:
+			internal_config.vhost_sock_owner = optarg;
+			break;
+
+		case OPT_VHOST_PERM_NUM:
+			internal_config.vhost_sock_perm = optarg;
+			break;
+
 		default:
 			if (opt < OPT_LONG_MIN_NUM && isprint(opt)) {
 				RTE_LOG(ERR, EAL, "Option %c is not supported "
@@ -934,3 +947,172 @@ rte_eal_check_module(const char *module_name)
 	/* Module has been found */
 	return 1;
 }
+
+/* Try to double the size of '*buf', return true
+ * if successful, and '*sizep' will be updated with
+ * the new size. Otherwise, return false.  */
+static int
+enlarge_buffer(char **buf, size_t *sizep)
+{
+    size_t newsize = *sizep * 2;
+
+    if (newsize > *sizep) {
+        *buf = realloc(*buf, newsize);
+        *sizep = newsize;
+        return 1;
+    }
+
+    return 0;
+}
+
+static int
+get_owners_from_str(const char *user_spec, uid_t *uid, gid_t *gid)
+{
+	size_t bufsize = 4096;
+
+	char *pos = strchr(user_spec, ':');
+	user_spec += strspn(user_spec, " \t\r\n");
+	size_t len = pos ? (size_t)(pos - user_spec) : strlen(user_spec);
+
+	char *buf = NULL;
+	struct passwd pwd, *res;
+	int e;
+
+	buf = malloc(bufsize);
+	char *user_search = NULL;
+	if (len) {
+		user_search = malloc(len + 1);
+		memcpy(user_search, user_spec, len);
+		user_search[len] = '\0';
+		while ((e = getpwnam_r(user_search, &pwd, buf, bufsize, &res)) == ERANGE) {
+			if (!enlarge_buffer(&buf, &bufsize)) {
+				break;
+			}
+		}
+
+		if (e != 0) {
+			RTE_LOG(ERR, EAL,"Failed to retrive user %s's uid (%s), aborting.",
+				user_search, strerror(e));
+			goto release;
+		}
+		if (res == NULL) {
+			RTE_LOG(ERR, EAL,"user %s not found,  aborting.",
+				user_search);
+			e = -1;
+			goto release;
+		}
+	} else {
+		/* User name is not specified, use current user.  */
+		while ((e = getpwuid_r(getuid(), &pwd, buf, bufsize, &res)) == ERANGE) {
+			if (!enlarge_buffer(&buf, &bufsize)) {
+				break;
+			}
+		}
+
+		if (e != 0) {
+			RTE_LOG(ERR, EAL,"Failed to retrive current user's uid "
+				"(%s), aborting.", strerror(e));
+			goto release;
+		}
+		user_search = strdup(pwd.pw_name);
+	}
+
+	if (uid)
+		*uid = pwd.pw_uid;
+
+	free(buf);
+	buf = NULL;
+
+	if (pos) {
+		char *grpstr = pos + 1;
+		grpstr += strspn(grpstr, " \t\r\n");
+
+		if (*grpstr) {
+			struct group grp, *res;
+
+			bufsize = 4096;
+			buf = malloc(bufsize);
+			while ((e = getgrnam_r(grpstr, &grp, buf, bufsize, &res))
+					 == ERANGE) {
+				if (!enlarge_buffer(&buf, &bufsize)) {
+					break;
+				}
+			}
+
+			if (e) {
+				RTE_LOG(ERR, EAL,"Failed to get group entry for %s, "
+					"(%s), aborting.", grpstr,
+					strerror(e));
+				goto release;
+			}
+			if (res == NULL) {
+				RTE_LOG(ERR, EAL,"Group %s not found, aborting.",
+					grpstr);
+				e = -1;
+				goto release;
+			}
+
+			if (gid)
+				*gid = grp.gr_gid;
+		}
+	}
+
+ release:
+	free(buf);
+	free(user_search);
+	return e;
+}
+
+static void
+vhost_set_permissions(const char *vhost_sock_location)
+{
+	unsigned long int mode = strtoul(internal_config.vhost_sock_perm, NULL, 0);
+	int err = chmod(vhost_sock_location, (mode_t)mode);
+	if (err) {
+		RTE_LOG(ERR, EAL,"vhost-user socket cannot set"
+			" permissions to %s (%s).\n",
+			internal_config.vhost_sock_perm, strerror(err));
+		return;
+	}
+	RTE_LOG(INFO, EAL,"Socket %s changed permissions"
+			" to %s\n", vhost_sock_location,
+			internal_config.vhost_sock_perm);
+}
+
+static void
+vhost_set_ownership(const char *vhost_sock_location)
+{
+	uid_t vhuid=0;
+	gid_t vhgid=0;
+
+	if (get_owners_from_str(internal_config.vhost_sock_owner, &vhuid, &vhgid)) {
+		RTE_LOG(ERR, EAL,"vhost-user socket unable to get"
+			" specified user/group: %s\n",
+			internal_config.vhost_sock_owner);
+		return;
+	}
+
+	int err = chown(vhost_sock_location, vhuid, vhgid);
+	if (err) {
+		RTE_LOG(ERR, EAL,"vhost-user socket unable to set"
+			" ownership to %s (%s).\n",
+			internal_config.vhost_sock_owner, strerror(err));
+		return;
+	}
+
+	RTE_LOG(INFO, EAL,"Socket %s changed ownership"
+			" to %s.\n", vhost_sock_location,
+			internal_config.vhost_sock_owner);
+}
+
+void
+rte_eal_set_socket_permissions(const char *path)
+{
+	if (internal_config.vhost_sock_perm) {
+		vhost_set_permissions(path);
+	}
+
+	if (internal_config.vhost_sock_owner) {
+		vhost_set_ownership(path);
+	}
+}
diff --git a/lib/librte_vhost/vhost_user/vhost-net-user.c b/lib/librte_vhost/vhost_user/vhost-net-user.c
index df2bd64..ea8dee9 100644
--- a/lib/librte_vhost/vhost_user/vhost-net-user.c
+++ b/lib/librte_vhost/vhost_user/vhost-net-user.c
@@ -51,6 +51,8 @@
 #include "vhost-net.h"
 #include "virtio-net-user.h"
 
+#include <rte_eal.h>
+
 #define MAX_VIRTIO_BACKLOG 128
 
 static void vserver_new_vq_conn(int fd, void *data, int *remove);
@@ -476,6 +478,8 @@ rte_vhost_driver_register(const char *path)
 		return -1;
 	}
 
+	rte_eal_set_socket_permissions(path);
+
 	vserver->path = strdup(path);
 
 	fdset_add(&g_vhost_server.fdset, vserver->listenfd,
-- 
2.7.4



More information about the dev mailing list