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. *Update* - the handling and lifecycle of this changed in Openvswitch 2.7 so we can no more use internal_config. - Also the upstreaming was aborted as that now clearly goes towards client mode vhost sockets for this (and other issues). - But until that is fully working we have to carry the workaround. - Updated to work with Openvswitch 2.7 (and backward compatible to 2.6) Forwarded: yes Author: Christian Ehrhardt Last-Update: 2017-05-23 --- 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 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 ---------------------------- --- 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 } }; --- 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 }; --- a/lib/librte_eal/common/include/rte_eal.h +++ b/lib/librte_eal/common/include/rte_eal.h @@ -286,6 +286,11 @@ static inline int rte_gettid(void) #define RTE_INIT(func) \ static void __attribute__((constructor, used)) func(void) +/** + * Set owner/permissions on sockets if requested on EAL commandline + */ +void rte_eal_set_socket_permissions(const char *); + #ifdef __cplusplus } #endif --- 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 @@ -119,6 +122,12 @@ struct lcore_config lcore_config[RTE_MAX /* internal configuration */ struct internal_config internal_config; +/* workaround to be able to create the sockets under a certain set of + * owner/permissions as specified to EAL until solved upstream */ +static uid_t debian_vhost_sock_uid = (uid_t)-1; +static gid_t debian_vhost_sock_gid = (gid_t)-1; +static mode_t debian_vhost_sock_perm = 0; + /* used by rte_rdtsc() */ int rte_cycles_vmware_tsc_map; @@ -356,6 +365,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 ) { @@ -515,6 +526,121 @@ eal_log_level_parse(int argc, char **arg optarg = old_optarg; } +/* 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; +} + /* Parse the argument given in the command line of the application */ static int eal_parse_args(int argc, char **argv) @@ -611,6 +737,26 @@ eal_parse_args(int argc, char **argv) internal_config.create_uio_dev = 1; break; + case OPT_VHOST_OWNER_NUM: + if (get_owners_from_str(optarg, &debian_vhost_sock_uid, + &debian_vhost_sock_gid)) { + RTE_LOG(ERR, EAL,"vhost-user socket unable to get" + " specified user/group: %s\n", optarg); + debian_vhost_sock_uid = (uid_t)-1; + debian_vhost_sock_gid = (gid_t)-1; + } + else { + RTE_LOG(INFO, EAL,"socket owner specified as %s (%d:%d)\n", + optarg, debian_vhost_sock_uid, debian_vhost_sock_gid); + } + break; + + case OPT_VHOST_PERM_NUM: + debian_vhost_sock_perm = (mode_t)strtoul(optarg, NULL, 0); + RTE_LOG(INFO, EAL,"socket perm specified as '%#o' from '%s'\n", + debian_vhost_sock_perm, optarg); + break; + default: if (opt < OPT_LONG_MIN_NUM && isprint(opt)) { RTE_LOG(ERR, EAL, "Option %c is not supported " @@ -995,3 +1141,47 @@ rte_eal_check_module(const char *module_ /* Module has been found */ return 1; } + +static void +vhost_set_permissions(const char *vhost_sock_location) +{ + int err = chmod(vhost_sock_location, debian_vhost_sock_perm); + if (err) { + RTE_LOG(ERR, EAL,"vhost-user socket cannot set" + " permissions to %#o (%s).\n", + debian_vhost_sock_perm, strerror(err)); + return; + } + RTE_LOG(INFO, EAL,"Socket %s changed permissions" + " to %#o\n", vhost_sock_location, + debian_vhost_sock_perm); +} + +static void +vhost_set_ownership(const char *vhost_sock_location) +{ + int err = chown(vhost_sock_location, debian_vhost_sock_uid, debian_vhost_sock_gid); + if (err) { + RTE_LOG(ERR, EAL,"vhost-user socket unable to set" + " ownership to %d:%d (%s).\n", + debian_vhost_sock_uid, debian_vhost_sock_gid, + strerror(err)); + return; + } + + RTE_LOG(INFO, EAL,"Socket %s changed ownership" + " to %d:%d.\n", vhost_sock_location, + debian_vhost_sock_uid, debian_vhost_sock_gid); +} + +void +rte_eal_set_socket_permissions(const char *path) +{ + if (debian_vhost_sock_perm != 0) { + vhost_set_permissions(path); + } + + if (debian_vhost_sock_uid != (uid_t)-1 || debian_vhost_sock_gid != (gid_t)-1) { + vhost_set_ownership(path); + } +} --- a/lib/librte_eal/linuxapp/eal/rte_eal_version.map +++ b/lib/librte_eal/linuxapp/eal/rte_eal_version.map @@ -120,6 +120,7 @@ DPDK_2.2 { rte_keepalive_register_core; rte_xen_dom0_supported; rte_xen_mem_phy2mch; + rte_eal_set_socket_permissions; } DPDK_2.1; --- a/lib/librte_vhost/socket.c +++ b/lib/librte_vhost/socket.c @@ -98,6 +98,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); @@ -644,7 +646,7 @@ rte_vhost_driver_register(const char *pa } else { vsocket->is_server = true; } - ret = create_unix_socket(vsocket); + rte_eal_set_socket_permissions(path); if (ret < 0) { free(vsocket->path); free(vsocket);