From patchwork Fri Oct 14 09:00:55 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stefan Hajnoczi X-Patchwork-Id: 682182 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3swMJQ3RLbz9sQw for ; Fri, 14 Oct 2016 20:10:30 +1100 (AEDT) Received: from localhost ([::1]:46153 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1buyVX-0001Ks-AS for incoming@patchwork.ozlabs.org; Fri, 14 Oct 2016 05:10:27 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:49786) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1buyMV-00025U-W1 for qemu-devel@nongnu.org; Fri, 14 Oct 2016 05:01:09 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1buyMU-0006IY-0K for qemu-devel@nongnu.org; Fri, 14 Oct 2016 05:01:07 -0400 Received: from mx1.redhat.com ([209.132.183.28]:45928) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1buyMT-0006IP-MZ for qemu-devel@nongnu.org; Fri, 14 Oct 2016 05:01:05 -0400 Received: from int-mx10.intmail.prod.int.phx2.redhat.com (int-mx10.intmail.prod.int.phx2.redhat.com [10.5.11.23]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 22DB93D94C; Fri, 14 Oct 2016 09:01:05 +0000 (UTC) Received: from localhost (ovpn-112-59.ams2.redhat.com [10.36.112.59]) by int-mx10.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id u9E913NL018590; Fri, 14 Oct 2016 05:01:04 -0400 From: Stefan Hajnoczi To: qemu-devel@nongnu.org Date: Fri, 14 Oct 2016 10:00:55 +0100 Message-Id: <1476435656-3100-4-git-send-email-stefanha@redhat.com> In-Reply-To: <1476435656-3100-1-git-send-email-stefanha@redhat.com> References: <1476435656-3100-1-git-send-email-stefanha@redhat.com> X-Scanned-By: MIMEDefang 2.68 on 10.5.11.23 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.30]); Fri, 14 Oct 2016 09:01:05 +0000 (UTC) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH v2 3/4] sockets: add AF_VSOCK support X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Michael Roth , Stefan Hajnoczi Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: "Qemu-devel" Add the AF_VSOCK address family so that qemu-ga will be able to use virtio-vsock. The AF_VSOCK address family uses address tuples. The cid is the unique identifier comparable to an IP address. AF_VSOCK does not use name resolution so it's easy to convert between struct sockaddr_vm and strings. This patch defines a VsockSocketAddress instead of trying to piggy-back on InetSocketAddress. This is cleaner in the long run since it avoids lots of IPv4 vs IPv6 vs vsock special casing. Signed-off-by: Stefan Hajnoczi --- v2: * s/seasy/easy/ typo fix in commit description [Eric] * Use %n to check for trailing characters in addresses [Eric] --- qapi-schema.json | 23 +++++- util/qemu-sockets.c | 227 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 249 insertions(+), 1 deletion(-) diff --git a/qapi-schema.json b/qapi-schema.json index 9e47b47..12aea99 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -988,12 +988,14 @@ # # @unix: unix socket # +# @vsock: vsock family (since 2.8) +# # @unknown: otherwise # # Since: 2.1 ## { 'enum': 'NetworkAddressFamily', - 'data': [ 'ipv4', 'ipv6', 'unix', 'unknown' ] } + 'data': [ 'ipv4', 'ipv6', 'unix', 'vsock', 'unknown' ] } ## # @VncBasicInfo @@ -3018,6 +3020,24 @@ 'path': 'str' } } ## +# @VsockSocketAddress +# +# Captures a socket address in the vsock namespace. +# +# @cid: unique host identifier +# @port: port +# +# Note that string types are used to allow for possible future hostname or +# service resolution support. +# +# Since 2.8 +## +{ 'struct': 'VsockSocketAddress', + 'data': { + 'cid': 'str', + 'port': 'str' } } + +## # @SocketAddress # # Captures the address of a socket, which could also be a named file descriptor @@ -3028,6 +3048,7 @@ 'data': { 'inet': 'InetSocketAddress', 'unix': 'UnixSocketAddress', + 'vsock': 'VsockSocketAddress', 'fd': 'String' } } ## diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c index 6db48b3..6ef3cc5 100644 --- a/util/qemu-sockets.c +++ b/util/qemu-sockets.c @@ -17,6 +17,10 @@ */ #include "qemu/osdep.h" +#ifdef AF_VSOCK +#include +#endif /* AF_VSOCK */ + #include "monitor/monitor.h" #include "qapi/error.h" #include "qemu/sockets.h" @@ -75,6 +79,9 @@ NetworkAddressFamily inet_netfamily(int family) case PF_INET6: return NETWORK_ADDRESS_FAMILY_IPV6; case PF_INET: return NETWORK_ADDRESS_FAMILY_IPV4; case PF_UNIX: return NETWORK_ADDRESS_FAMILY_UNIX; +#ifdef AF_VSOCK + case PF_VSOCK: return NETWORK_ADDRESS_FAMILY_VSOCK; +#endif /* AF_VSOCK */ } return NETWORK_ADDRESS_FAMILY_UNKNOWN; } @@ -650,6 +657,181 @@ int inet_connect(const char *str, Error **errp) return sock; } +#ifdef AF_VSOCK +static bool vsock_parse_vaddr_to_sockaddr(const VsockSocketAddress *vaddr, + struct sockaddr_vm *svm, + Error **errp) +{ + unsigned long long val; + + memset(svm, 0, sizeof(*svm)); + svm->svm_family = AF_VSOCK; + + if (parse_uint_full(vaddr->cid, &val, 10) < 0 || + val > UINT32_MAX) { + error_setg(errp, "Failed to parse cid '%s'", vaddr->cid); + return false; + } + svm->svm_cid = val; + + if (parse_uint_full(vaddr->port, &val, 10) < 0 || + val > UINT32_MAX) { + error_setg(errp, "Failed to parse port '%s'", vaddr->port); + return false; + } + svm->svm_port = val; + + return true; +} + +static int vsock_connect_addr(const struct sockaddr_vm *svm, bool *in_progress, + ConnectState *connect_state, Error **errp) +{ + int sock, rc; + + *in_progress = false; + + sock = qemu_socket(AF_VSOCK, SOCK_STREAM, 0); + if (sock < 0) { + error_setg_errno(errp, errno, "Failed to create socket"); + return -1; + } + if (connect_state != NULL) { + qemu_set_nonblock(sock); + } + /* connect to peer */ + do { + rc = 0; + if (connect(sock, (const struct sockaddr *)svm, sizeof(*svm)) < 0) { + rc = -errno; + } + } while (rc == -EINTR); + + if (connect_state != NULL && QEMU_SOCKET_RC_INPROGRESS(rc)) { + connect_state->fd = sock; + qemu_set_fd_handler(sock, NULL, wait_for_connect, connect_state); + *in_progress = true; + } else if (rc < 0) { + error_setg_errno(errp, errno, "Failed to connect socket"); + closesocket(sock); + return -1; + } + return sock; +} + +static int vsock_connect_saddr(VsockSocketAddress *vaddr, Error **errp, + NonBlockingConnectHandler *callback, + void *opaque) +{ + struct sockaddr_vm svm; + int sock = -1; + bool in_progress; + ConnectState *connect_state = NULL; + + if (!vsock_parse_vaddr_to_sockaddr(vaddr, &svm, errp)) { + return -1; + } + + if (callback != NULL) { + connect_state = g_malloc0(sizeof(*connect_state)); + connect_state->callback = callback; + connect_state->opaque = opaque; + } + + sock = vsock_connect_addr(&svm, &in_progress, connect_state, errp); + if (sock < 0) { + /* do nothing */ + } else if (in_progress) { + /* wait_for_connect() will do the rest */ + return sock; + } else { + if (callback) { + callback(sock, NULL, opaque); + } + } + g_free(connect_state); + return sock; +} + +static int vsock_listen_saddr(VsockSocketAddress *vaddr, + Error **errp) +{ + struct sockaddr_vm svm; + int slisten; + + if (!vsock_parse_vaddr_to_sockaddr(vaddr, &svm, errp)) { + return -1; + } + + slisten = qemu_socket(AF_VSOCK, SOCK_STREAM, 0); + if (slisten < 0) { + error_setg_errno(errp, errno, "Failed to create socket"); + return -1; + } + + if (bind(slisten, (const struct sockaddr *)&svm, sizeof(svm)) != 0) { + error_setg_errno(errp, errno, "Failed to bind socket"); + closesocket(slisten); + return -1; + } + + if (listen(slisten, 1) != 0) { + error_setg_errno(errp, errno, "Failed to listen on socket"); + closesocket(slisten); + return -1; + } + return slisten; +} + +static VsockSocketAddress *vsock_parse(const char *str, Error **errp) +{ + VsockSocketAddress *addr = NULL; + char cid[33]; + char port[33]; + int n; + + if (sscanf(str, "%32[^:]:%32[^,]%n", cid, port, &n) != 2) { + error_setg(errp, "error parsing address '%s'", str); + return NULL; + } + if (str[n] != '\0') { + error_setg(errp, "trailing characters in address '%s'", str); + return NULL; + } + + addr = g_new0(VsockSocketAddress, 1); + addr->cid = g_strdup(cid); + addr->port = g_strdup(port); + return addr; +} +#else +static void vsock_unsupported(Error **errp) +{ + error_setg(errp, "socket family AF_VSOCK unsupported"); +} + +static int vsock_connect_saddr(VsockSocketAddress *vaddr, Error **errp, + NonBlockingConnectHandler *callback, + void *opaque) +{ + vsock_unsupported(errp); + return -1; +} + +static int vsock_listen_saddr(VsockSocketAddress *vaddr, + Error **errp) +{ + vsock_unsupported(errp); + return -1; +} + +static VsockSocketAddress *vsock_parse(const char *str, Error **errp) +{ + vsock_unsupported(errp); + return NULL; +} +#endif /* AF_VSOCK */ + #ifndef _WIN32 static int unix_listen_saddr(UnixSocketAddress *saddr, @@ -864,6 +1046,12 @@ SocketAddress *socket_parse(const char *str, Error **errp) addr->u.fd.data = g_new(String, 1); addr->u.fd.data->str = g_strdup(str + 3); } + } else if (strstart(str, "vsock:", NULL)) { + addr->type = SOCKET_ADDRESS_KIND_VSOCK; + addr->u.vsock.data = vsock_parse(str + strlen("vsock:"), errp); + if (addr->u.vsock.data == NULL) { + goto fail; + } } else { addr->type = SOCKET_ADDRESS_KIND_INET; addr->u.inet.data = inet_parse(str, errp); @@ -900,6 +1088,10 @@ int socket_connect(SocketAddress *addr, Error **errp, } break; + case SOCKET_ADDRESS_KIND_VSOCK: + fd = vsock_connect_saddr(addr->u.vsock.data, errp, callback, opaque); + break; + default: abort(); } @@ -923,6 +1115,10 @@ int socket_listen(SocketAddress *addr, Error **errp) fd = monitor_get_fd(cur_mon, addr->u.fd.data->str, errp); break; + case SOCKET_ADDRESS_KIND_VSOCK: + fd = vsock_listen_saddr(addr->u.vsock.data, errp); + break; + default: abort(); } @@ -1022,6 +1218,26 @@ socket_sockaddr_to_address_unix(struct sockaddr_storage *sa, } #endif /* WIN32 */ +#ifdef AF_VSOCK +static SocketAddress * +socket_sockaddr_to_address_vsock(struct sockaddr_storage *sa, + socklen_t salen, + Error **errp) +{ + SocketAddress *addr; + VsockSocketAddress *vaddr; + struct sockaddr_vm *svm = (struct sockaddr_vm *)sa; + + addr = g_new0(SocketAddress, 1); + addr->type = SOCKET_ADDRESS_KIND_VSOCK; + addr->u.vsock.data = vaddr = g_new0(VsockSocketAddress, 1); + vaddr->cid = g_strdup_printf("%u", svm->svm_cid); + vaddr->port = g_strdup_printf("%u", svm->svm_port); + + return addr; +} +#endif /* AF_VSOCK */ + SocketAddress * socket_sockaddr_to_address(struct sockaddr_storage *sa, socklen_t salen, @@ -1037,6 +1253,11 @@ socket_sockaddr_to_address(struct sockaddr_storage *sa, return socket_sockaddr_to_address_unix(sa, salen, errp); #endif /* WIN32 */ +#ifdef AF_VSOCK + case AF_VSOCK: + return socket_sockaddr_to_address_vsock(sa, salen, errp); +#endif + default: error_setg(errp, "socket family %d unsupported", sa->ss_family); @@ -1103,6 +1324,12 @@ char *socket_address_to_string(struct SocketAddress *addr, Error **errp) buf = g_strdup(addr->u.fd.data->str); break; + case SOCKET_ADDRESS_KIND_VSOCK: + buf = g_strdup_printf("%s:%s", + addr->u.vsock.data->cid, + addr->u.vsock.data->port); + break; + default: error_setg(errp, "socket family %d unsupported", addr->type);