Description: eal: provide option to set vhost_user socket owner/permissions The API doesn't hold a way to specify a owner/permission set for vhost_user created sockets. Projects consuming DPDK started 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/). We need something now for existing code linking against DPDK. That implies to avoid changing API/ABI. So I created a DPDK EAL commandline option based ideas in the former patches. Fixes LP: #1546565 *Update* - with the split libs it now nees to be listed in lib/librte_eal/linuxapp/eal/rte_eal_version.map to work on link steps - please note that upstream gravitates towards not extending but creating a new the API in DPDK as long term solution (will take a while) - also as listed before most affected projects seem to create their own workaround. So over time we have to check when we can drop it at the price of a config transition - likely OVS 2.6 won't need it anymore. Forwarded: yes Author: Christian Ehrhardt Last-Update: 2016-07-25 diff --git a/doc/guides/testpmd_app_ug/run_app.rst b/doc/guides/testpmd_app_ug/run_app.rst index 7712bd2..28776b9 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 481c732..2126140 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 } }; @@ -166,6 +168,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 3fb2188..dc84c5b 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 #endif +#include +#include +#include #include #include @@ -344,6 +347,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 ) { @@ -601,6 +606,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 " @@ -943,3 +956,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_eal/linuxapp/eal/rte_eal_version.map b/lib/librte_eal/linuxapp/eal/rte_eal_version.map index db8c984..bb92e57 100644 --- a/lib/librte_eal/linuxapp/eal/rte_eal_version.map +++ b/lib/librte_eal/linuxapp/eal/rte_eal_version.map @@ -139,6 +139,7 @@ DPDK_2.2 { rte_keepalive_register_core; rte_xen_dom0_supported; rte_xen_mem_phy2mch; + rte_eal_set_socket_permissions; } DPDK_2.1; diff --git a/lib/librte_vhost/vhost_user/vhost-net-user.c b/lib/librte_vhost/vhost_user/vhost-net-user.c index b35594d..dbdb8ad 100644 --- a/lib/librte_vhost/vhost_user/vhost-net-user.c +++ b/lib/librte_vhost/vhost_user/vhost-net-user.c @@ -79,6 +79,8 @@ struct vhost_user { pthread_mutex_t mutex; }; +#include + #define MAX_VIRTIO_BACKLOG 128 static void vhost_user_server_new_connection(int fd, void *data, int *remove); @@ -682,6 +684,7 @@ rte_vhost_driver_register(const char *path, uint64_t flags) if (!vsocket) goto out; memset(vsocket, 0, sizeof(struct vhost_user_socket)); + rte_eal_set_socket_permissions(path); vsocket->path = strdup(path); vsocket->connfd = -1;