Message ID | 1476435656-3100-4-git-send-email-stefanha@redhat.com |
---|---|
State | New |
Headers | show |
On 10/14/2016 04:00 AM, Stefan Hajnoczi wrote: > Add the AF_VSOCK address family so that qemu-ga will be able to use > virtio-vsock. > > The AF_VSOCK address family uses <cid, port> 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 <stefanha@redhat.com> > --- > ## > +# @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' } } It would also be possible to do this now: { 'struct': 'VsockSocketAddress', 'data': { 'cid': 'int', 'port': 'int' } } then down the road expand to: { 'alternate': 'StrOrInt', 'data': { 's': 'str', 'i': 'int' } } { 'struct': 'VsockSocketAddress', 'data': { 'cid': 'StrOrInt', 'port': 'StrOrInt' } } although the C code to do type-safe access vsock->cid.u.i or vsock->cid.u.s based on vsock->cid.type is a bit more verbose than just accessing vsock->cid as a string and converting every time around. Where it gets really interesting is that using the alternate would allow all of these QMP forms: { "socket": { "cid": 1, "port": 2 } } { "socket": { "cid": "1", "port": "2" } } { "socket": { "cid": "host", "port": "service" } } It MIGHT be worth going the type-safe route, especially if we WANT to convert the existing SocketAddress uses to use the alternate rather than always managing things as a string. But it doesn't have to be in this patch. > +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) { This says stop at the first comma after the colon... > + error_setg(errp, "error parsing address '%s'", str); > + return NULL; > + } > + if (str[n] != '\0') { > + error_setg(errp, "trailing characters in address '%s'", str); ...but this rejects a trailing comma. Is a trailing comma possible base on how QemuOpts work? If so, do you need to handle it here? Otherwise looking okay to me.
On Fri, Oct 14, 2016 at 09:40:40AM -0500, Eric Blake wrote: > On 10/14/2016 04:00 AM, Stefan Hajnoczi wrote: > > Add the AF_VSOCK address family so that qemu-ga will be able to use > > virtio-vsock. > > > > The AF_VSOCK address family uses <cid, port> 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 <stefanha@redhat.com> > > --- > > > ## > > +# @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' } } > > It would also be possible to do this now: > > { 'struct': 'VsockSocketAddress', > 'data': { 'cid': 'int', 'port': 'int' } } > > then down the road expand to: > > { 'alternate': 'StrOrInt', > 'data': { 's': 'str', 'i': 'int' } } > { 'struct': 'VsockSocketAddress', > 'data': { 'cid': 'StrOrInt', 'port': 'StrOrInt' } } > > although the C code to do type-safe access vsock->cid.u.i or > vsock->cid.u.s based on vsock->cid.type is a bit more verbose than just > accessing vsock->cid as a string and converting every time around. > Where it gets really interesting is that using the alternate would allow > all of these QMP forms: > > { "socket": { "cid": 1, "port": 2 } } > { "socket": { "cid": "1", "port": "2" } } > { "socket": { "cid": "host", "port": "service" } } > > It MIGHT be worth going the type-safe route, especially if we WANT to > convert the existing SocketAddress uses to use the alternate rather than > always managing things as a string. But it doesn't have to be in this > patch. > > > +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) { > > This says stop at the first comma after the colon... > > > + error_setg(errp, "error parsing address '%s'", str); > > + return NULL; > > + } > > + if (str[n] != '\0') { > > + error_setg(errp, "trailing characters in address '%s'", str); > > ...but this rejects a trailing comma. Is a trailing comma possible base > on how QemuOpts work? If so, do you need to handle it here? Actually I just wanted to grab characters up until the end of string. It wasn't clear from the sscanf(3) man page what the best way to do that was, so I kept the comma which is also used in tcp addresses (because they support additional comma-separated options). Stefan
On 10/16/2016 08:35 AM, Stefan Hajnoczi wrote: >>> + >>> + if (sscanf(str, "%32[^:]:%32[^,]%n", cid, port, &n) != 2) { >> >> This says stop at the first comma after the colon... >> >>> + error_setg(errp, "error parsing address '%s'", str); >>> + return NULL; >>> + } >>> + if (str[n] != '\0') { >>> + error_setg(errp, "trailing characters in address '%s'", str); >> >> ...but this rejects a trailing comma. Is a trailing comma possible base >> on how QemuOpts work? If so, do you need to handle it here? > > Actually I just wanted to grab characters up until the end of string. > It wasn't clear from the sscanf(3) man page what the best way to do that > was, so I kept the comma which is also used in tcp addresses (because > they support additional comma-separated options). %32s instead of %32[^,] should grab up to all 32 remaining characters in the string; your %n trick then ensures there is no garbage. I guess it's still a question of whether we want to always treat a comma as trailing garbage.
On Mon, Oct 17, 2016 at 09:25:46AM -0500, Eric Blake wrote: > On 10/16/2016 08:35 AM, Stefan Hajnoczi wrote: > > >>> + > >>> + if (sscanf(str, "%32[^:]:%32[^,]%n", cid, port, &n) != 2) { > >> > >> This says stop at the first comma after the colon... > >> > >>> + error_setg(errp, "error parsing address '%s'", str); > >>> + return NULL; > >>> + } > >>> + if (str[n] != '\0') { > >>> + error_setg(errp, "trailing characters in address '%s'", str); > >> > >> ...but this rejects a trailing comma. Is a trailing comma possible base > >> on how QemuOpts work? If so, do you need to handle it here? > > > > Actually I just wanted to grab characters up until the end of string. > > It wasn't clear from the sscanf(3) man page what the best way to do that > > was, so I kept the comma which is also used in tcp addresses (because > > they support additional comma-separated options). > > %32s instead of %32[^,] should grab up to all 32 remaining characters in > the string; your %n trick then ensures there is no garbage. I guess > it's still a question of whether we want to always treat a comma as > trailing garbage. No comma options are currently accepted. Treating comma as garbage seems okay. Mike: Please change %32[^,] to %32s when merging. Thanks! Stefan
Quoting Stefan Hajnoczi (2016-10-14 04:00:55) > Add the AF_VSOCK address family so that qemu-ga will be able to use > virtio-vsock. > > The AF_VSOCK address family uses <cid, port> 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 <stefanha@redhat.com> > --- > 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 <linux/vm_sockets.h> > +#endif /* AF_VSOCK */ I have this series applied locally but I hit some build issues on Ubuntu 14.04 due to linux/vm_sockets.h not being provided by Ubuntu 14.04's linux-libc-dev package. It is however included with linux-libc-dev in 16.04. linux-headers package includes it in both cases, but installs to /usr/src/linux-headers*, which are not part of the default include path. Do you think we need a configure check and CONFIG_AF_VSOCK flag instead? > + > #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); > -- > 2.7.4 >
[Reviving a more than 1-year old thread. I randomly noticed this (refer below) while browsing through AF_VSOCK changes in my 'qemu-devel' maildir.] On Tue, Oct 18, 2016 at 11:21:23AM +0100, Stefan Hajnoczi wrote: > On Mon, Oct 17, 2016 at 09:25:46AM -0500, Eric Blake wrote: > > On 10/16/2016 08:35 AM, Stefan Hajnoczi wrote: > > > > >>> + > > >>> + if (sscanf(str, "%32[^:]:%32[^,]%n", cid, port, &n) != 2) { > > >> > > >> This says stop at the first comma after the colon... > > >> > > >>> + error_setg(errp, "error parsing address '%s'", str); > > >>> + return NULL; > > >>> + } > > >>> + if (str[n] != '\0') { > > >>> + error_setg(errp, "trailing characters in address '%s'", str); > > >> > > >> ...but this rejects a trailing comma. Is a trailing comma possible base > > >> on how QemuOpts work? If so, do you need to handle it here? > > > > > > Actually I just wanted to grab characters up until the end of string. > > > It wasn't clear from the sscanf(3) man page what the best way to do that > > > was, so I kept the comma which is also used in tcp addresses (because > > > they support additional comma-separated options). > > > > %32s instead of %32[^,] should grab up to all 32 remaining characters in > > the string; your %n trick then ensures there is no garbage. I guess > > it's still a question of whether we want to always treat a comma as > > trailing garbage. > > No comma options are currently accepted. Treating comma as garbage > seems okay. > > Mike: Please change %32[^,] to %32s when merging. Thanks! The above change didn't take place while merging at that time as part of v2.7.0-1720-g6a02c80. I still see %32[^,] in all the sscanf(3) calls in util/util/qemu-sockets.c Is it still important to change?
On Sun, Nov 05, 2017 at 07:59:59PM +0100, Kashyap Chamarthy wrote: > [Reviving a more than 1-year old thread. I randomly noticed this (refer > below) while browsing through AF_VSOCK changes in my 'qemu-devel' > maildir.] > > On Tue, Oct 18, 2016 at 11:21:23AM +0100, Stefan Hajnoczi wrote: > > On Mon, Oct 17, 2016 at 09:25:46AM -0500, Eric Blake wrote: > > > On 10/16/2016 08:35 AM, Stefan Hajnoczi wrote: > > > > > > >>> + > > > >>> + if (sscanf(str, "%32[^:]:%32[^,]%n", cid, port, &n) != 2) { > > > >> > > > >> This says stop at the first comma after the colon... > > > >> > > > >>> + error_setg(errp, "error parsing address '%s'", str); > > > >>> + return NULL; > > > >>> + } > > > >>> + if (str[n] != '\0') { > > > >>> + error_setg(errp, "trailing characters in address '%s'", str); > > > >> > > > >> ...but this rejects a trailing comma. Is a trailing comma possible base > > > >> on how QemuOpts work? If so, do you need to handle it here? > > > > > > > > Actually I just wanted to grab characters up until the end of string. > > > > It wasn't clear from the sscanf(3) man page what the best way to do that > > > > was, so I kept the comma which is also used in tcp addresses (because > > > > they support additional comma-separated options). > > > > > > %32s instead of %32[^,] should grab up to all 32 remaining characters in > > > the string; your %n trick then ensures there is no garbage. I guess > > > it's still a question of whether we want to always treat a comma as > > > trailing garbage. > > > > No comma options are currently accepted. Treating comma as garbage > > seems okay. > > > > Mike: Please change %32[^,] to %32s when merging. Thanks! > > The above change didn't take place while merging at that time as part of > v2.7.0-1720-g6a02c80. > > I still see %32[^,] in all the sscanf(3) calls in > util/util/qemu-sockets.c > > Is it still important to change? Thanks for noticing. I don't think it's necessary to make this change now. Stefan
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 <linux/vm_sockets.h> +#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);
Add the AF_VSOCK address family so that qemu-ga will be able to use virtio-vsock. The AF_VSOCK address family uses <cid, port> 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 <stefanha@redhat.com> --- 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(-)