From ba51349d353f11e05c6341a7e065f2ade3874c68 Mon Sep 17 00:00:00 2001
Message-Id: <ba51349d353f11e05c6341a7e065f2ade3874c68.1619091389.git.sbrivio@redhat.com>
From: Stefano Brivio <sbrivio@redhat.com>
Date: Wed, 21 Apr 2021 18:51:18 +0200
Subject: [PATCH 1/2] net: Allow also UNIX domain sockets to be used as -netdev
socket
It has lower overhead compared to TCP, doesn't need a free port
and the adaptation is trivial.
Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
---
SPDX-FileCopyrightText: 2020-2021 Red Hat GmbH <sbrivio@redhat.com>
SPDX-License-Identifier: AGPL-3.0-or-later
net/socket.c | 106 ++++++++++++++++++++++++++++++++++++++++--------
qemu-options.hx | 12 +++---
2 files changed, 94 insertions(+), 24 deletions(-)
diff --git a/net/socket.c b/net/socket.c
index 15b410e8d825..aadd11dae2b3 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -511,26 +511,60 @@ static int net_socket_listen_init(NetClientState *peer,
{
NetClientState *nc;
NetSocketState *s;
- struct sockaddr_in saddr;
- int fd, ret;
+ struct sockaddr_storage saddr;
+ struct sockaddr_in *saddr_in = (struct sockaddr_in *)&saddr;
+ struct sockaddr_un *saddr_un = (struct sockaddr_un *)&saddr;
+ size_t saddr_size;
+ int fd, ret, pf;
+
+#ifndef WIN32
+ if (strchr(host_str, ':')) {
+#endif
+ if (parse_host_port(saddr_in, host_str, errp) < 0)
+ return -1;
- if (parse_host_port(&saddr, host_str, errp) < 0) {
- return -1;
+ pf = PF_INET;
+ saddr_size = sizeof(*saddr_in);
+#ifndef WIN32
+ } else {
+ struct stat sb;
+
+ if (stat(host_str, &sb) == -1) {
+ error_setg_errno(errp, errno, "can't stat socket path");
+ return -1;
+ }
+
+ if ((sb.st_mode & S_IFMT) != S_IFSOCK) {
+ error_setg_errno(errp, errno, "path provided is not a socket");
+ return -1;
+ }
+
+ saddr_un->sun_family = PF_UNIX;
+ strncpy(saddr_un->sun_path, host_str, sizeof(saddr_un->sun_path));
+
+ pf = PF_UNIX;
+ saddr_size = sizeof(*saddr_un);
}
+#endif /* !WIN32 */
- fd = qemu_socket(PF_INET, SOCK_STREAM, 0);
+ fd = qemu_socket(pf, SOCK_STREAM, 0);
if (fd < 0) {
error_setg_errno(errp, errno, "can't create stream socket");
return -1;
}
qemu_set_nonblock(fd);
- socket_set_fast_reuse(fd);
+ if (pf == PF_INET)
+ socket_set_fast_reuse(fd);
- ret = bind(fd, (struct sockaddr *)&saddr, sizeof(saddr));
+ ret = bind(fd, (struct sockaddr *)&saddr, saddr_size);
if (ret < 0) {
- error_setg_errno(errp, errno, "can't bind ip=%s to socket",
- inet_ntoa(saddr.sin_addr));
+ if (pf == PF_INET)
+ error_setg_errno(errp, errno, "can't bind ip=%s to socket",
+ inet_ntoa(saddr_in->sin_addr));
+ else if (pf == PF_UNIX)
+ error_setg_errno(errp, errno, "can't create socket with path: %s",
+ host_str);
closesocket(fd);
return -1;
}
@@ -559,14 +593,44 @@ static int net_socket_connect_init(NetClientState *peer,
Error **errp)
{
NetSocketState *s;
- int fd, connected, ret;
- struct sockaddr_in saddr;
+ int fd, connected, ret, pf;
+ struct sockaddr_storage saddr;
+ size_t saddr_size;
+ struct sockaddr_in *saddr_in = (struct sockaddr_in *)&saddr;
+#ifndef WIN32
+ struct sockaddr_un *saddr_un = (struct sockaddr_un *)&saddr;
- if (parse_host_port(&saddr, host_str, errp) < 0) {
- return -1;
+
+ if (strchr(host_str, ':')) {
+#endif
+ if (parse_host_port(saddr_in, host_str, errp) < 0)
+ return -1;
+
+ pf = PF_INET;
+ saddr_size = sizeof(*saddr_in);
+#ifndef WIN32
+ } else {
+ struct stat sb;
+
+ if (stat(host_str, &sb) == -1) {
+ error_setg_errno(errp, errno, "can't stat socket path");
+ return -1;
+ }
+
+ if ((sb.st_mode & S_IFMT) != S_IFSOCK) {
+ error_setg_errno(errp, errno, "provided path is not a socket");
+ return -1;
+ }
+
+ saddr_un->sun_family = PF_UNIX;
+ strncpy(saddr_un->sun_path, host_str, sizeof(saddr_un->sun_path));
+
+ pf = PF_UNIX;
+ saddr_size = sizeof(*saddr_un);
}
+#endif /* !WIN32 */
- fd = qemu_socket(PF_INET, SOCK_STREAM, 0);
+ fd = qemu_socket(pf, SOCK_STREAM, 0);
if (fd < 0) {
error_setg_errno(errp, errno, "can't create stream socket");
return -1;
@@ -575,7 +639,7 @@ static int net_socket_connect_init(NetClientState *peer,
connected = 0;
for(;;) {
- ret = connect(fd, (struct sockaddr *)&saddr, sizeof(saddr));
+ ret = connect(fd, (struct sockaddr *)&saddr, saddr_size);
if (ret < 0) {
if (errno == EINTR || errno == EWOULDBLOCK) {
/* continue */
@@ -598,9 +662,15 @@ static int net_socket_connect_init(NetClientState *peer,
return -1;
}
- snprintf(s->nc.info_str, sizeof(s->nc.info_str),
- "socket: connect to %s:%d",
- inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
+ if (pf == PF_INET) {
+ snprintf(s->nc.info_str, sizeof(s->nc.info_str),
+ "socket: connect to %s:%d",
+ inet_ntoa(saddr_in->sin_addr), ntohs(saddr_in->sin_port));
+ } else if (pf == PF_UNIX) {
+ snprintf(s->nc.info_str, sizeof(s->nc.info_str),
+ "socket: connect to %s", saddr_un->sun_path);
+ }
+
return 0;
}
diff --git a/qemu-options.hx b/qemu-options.hx
index fd21002bd61d..625a31dcdbc8 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -2847,13 +2847,13 @@ SRST
#connect a TAP device to bridge qemubr0
|qemu_system| linux.img -netdev bridge,br=qemubr0,id=n1 -device virtio-net,netdev=n1
-``-netdev socket,id=id[,fd=h][,listen=[host]:port][,connect=host:port]``
+``-netdev socket,id=id[,fd=h][,listen=[host]:port|path][,connect=host:port|path]``
This host network backend can be used to connect the guest's network
- to another QEMU virtual machine using a TCP socket connection. If
- ``listen`` is specified, QEMU waits for incoming connections on port
- (host is optional). ``connect`` is used to connect to another QEMU
- instance using the ``listen`` option. ``fd``\ =h specifies an
- already opened TCP socket.
+ to another QEMU virtual machine using a TCP or a UNIX domain socket
+ connection. If ``listen`` is specified, QEMU waits for incoming
+ connections on port (host is optional), or on path. ``connect`` is used
+ to connect to another QEMU instance using the ``listen`` option.
+ ``fd``\ =h specifies an already opened TCP or UNIX domain socket.
Example:
--
2.29.2