sockets: add AF_VSOCK support
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> * treat trailing commas as garbage when parsing (Eric Blake) * add configure check instead of checking AF_VSOCK directly Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
This commit is contained in:
		
							parent
							
								
									f06b2031a3
								
							
						
					
					
						commit
						6a02c8069f
					
				| 
						 | 
				
			
			@ -4674,6 +4674,33 @@ if compile_prog "" "" ; then
 | 
			
		|||
    have_rtnetlink=yes
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
##########################################
 | 
			
		||||
# check for usable AF_VSOCK environment
 | 
			
		||||
have_af_vsock=no
 | 
			
		||||
cat > $TMPC << EOF
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/socket.h>
 | 
			
		||||
#if !defined(AF_VSOCK)
 | 
			
		||||
# error missing AF_VSOCK flag
 | 
			
		||||
#endif
 | 
			
		||||
#include <linux/vm_sockets.h>
 | 
			
		||||
int main(void) {
 | 
			
		||||
    int sock, ret;
 | 
			
		||||
    struct sockaddr_vm svm;
 | 
			
		||||
    socklen_t len = sizeof(svm);
 | 
			
		||||
    sock = socket(AF_VSOCK, SOCK_STREAM, 0);
 | 
			
		||||
    ret = getpeername(sock, (struct sockaddr *)&svm, &len);
 | 
			
		||||
    if ((ret == -1) && (errno == ENOTCONN)) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
EOF
 | 
			
		||||
if compile_prog "" "" ; then
 | 
			
		||||
    have_af_vsock=yes
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
#################################################
 | 
			
		||||
# Sparc implicitly links with --relax, which is
 | 
			
		||||
# incompatible with -r, so --no-relax should be
 | 
			
		||||
| 
						 | 
				
			
			@ -5662,6 +5689,10 @@ if test "$replication" = "yes" ; then
 | 
			
		|||
  echo "CONFIG_REPLICATION=y" >> $config_host_mak
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if test "$have_af_vsock" = "yes" ; then
 | 
			
		||||
  echo "CONFIG_AF_VSOCK=y" >> $config_host_mak
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
# Hold two types of flag:
 | 
			
		||||
#   CONFIG_THREAD_SETNAME_BYTHREAD  - we've got a way of setting the name on
 | 
			
		||||
#                                     a thread we have a handle to
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1063,12 +1063,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
 | 
			
		||||
| 
						 | 
				
			
			@ -3094,6 +3096,24 @@
 | 
			
		|||
  'data': {
 | 
			
		||||
    '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
 | 
			
		||||
#
 | 
			
		||||
| 
						 | 
				
			
			@ -3105,6 +3125,7 @@
 | 
			
		|||
  'data': {
 | 
			
		||||
    'inet': 'InetSocketAddress',
 | 
			
		||||
    'unix': 'UnixSocketAddress',
 | 
			
		||||
    'vsock': 'VsockSocketAddress',
 | 
			
		||||
    'fd': 'String' } }
 | 
			
		||||
 | 
			
		||||
##
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,6 +17,10 @@
 | 
			
		|||
 */
 | 
			
		||||
#include "qemu/osdep.h"
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_AF_VSOCK
 | 
			
		||||
#include <linux/vm_sockets.h>
 | 
			
		||||
#endif /* CONFIG_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 CONFIG_AF_VSOCK
 | 
			
		||||
    case PF_VSOCK: return NETWORK_ADDRESS_FAMILY_VSOCK;
 | 
			
		||||
#endif /* CONFIG_AF_VSOCK */
 | 
			
		||||
    }
 | 
			
		||||
    return NETWORK_ADDRESS_FAMILY_UNKNOWN;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -650,6 +657,181 @@ int inet_connect(const char *str, Error **errp)
 | 
			
		|||
    return sock;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_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 /* CONFIG_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 CONFIG_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 /* CONFIG_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 CONFIG_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);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue