From 8bfcc9208cf99a653b9235bbe28ad5eb6c2e6bf2 Mon Sep 17 00:00:00 2001 From: Stefano Brivio Date: Wed, 17 Mar 2021 10:57:45 +0100 Subject: [PATCH] passt: qemu patch for direct UNIX domain connection without the qrap wrapper ...and, while at it, a second patch to fail when connect() fails in turn with EINVAL. These two patches haven't been sent upstream yet. Signed-off-by: Stefano Brivio --- passt.c | 7 +- ...NIX-domain-sockets-to-be-used-as-net.patch | 176 ++++++++++++++++++ ...e-EINVAL-on-netdev-socket-connection.patch | 31 +++ 3 files changed, 213 insertions(+), 1 deletion(-) create mode 100644 qemu/0001-net-Allow-also-UNIX-domain-sockets-to-be-used-as-net.patch create mode 100644 qemu/0002-net-Don-t-ignore-EINVAL-on-netdev-socket-connection.patch diff --git a/passt.c b/passt.c index 98576a6..6bc040a 100644 --- a/passt.c +++ b/passt.c @@ -562,7 +562,12 @@ listen: listen(fd_unix, 1); fprintf(stderr, "You can now start qrap:\n\t" - "./qrap 5 kvm ... -net socket,fd=5 -net nic,model=virtio\n\n"); + "./qrap 5 kvm ... -net socket,fd=5 -net nic,model=virtio\n" + "or directly qemu, patched with:\n\t" + "qemu/0001-net-Allow-also-UNIX-domain-sockets-to-be-used-as-net.patch\n" + "as follows:\n\t" + "kvm ... -net socket,connect=" + UNIX_SOCK_PATH " -net nic,model=virtio\n\n"); c.fd_unix = accept(fd_unix, NULL, NULL); ev.events = EPOLLIN | EPOLLET | EPOLLRDHUP | EPOLLERR | EPOLLHUP; diff --git a/qemu/0001-net-Allow-also-UNIX-domain-sockets-to-be-used-as-net.patch b/qemu/0001-net-Allow-also-UNIX-domain-sockets-to-be-used-as-net.patch new file mode 100644 index 0000000..ef00526 --- /dev/null +++ b/qemu/0001-net-Allow-also-UNIX-domain-sockets-to-be-used-as-net.patch @@ -0,0 +1,176 @@ +From 8876676bbdb108f51c372d1347dec1382078d702 Mon Sep 17 00:00:00 2001 +From: Stefano Brivio +Date: Wed, 17 Mar 2021 10:33:22 +0100 +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 +--- + net/socket.c | 93 +++++++++++++++++++++++++++++++++++++++++-------- + qemu-options.hx | 12 +++---- + 2 files changed, 84 insertions(+), 21 deletions(-) + +diff --git a/net/socket.c b/net/socket.c +index c0de10c0c0..9d06953983 100644 +--- a/net/socket.c ++++ b/net/socket.c +@@ -528,27 +528,62 @@ 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; + NetdevSocketOptions *stored; + +- if (parse_host_port(&saddr, host_str, errp) < 0) { +- return -1; ++#ifndef WIN32 ++ 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, "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; + } +@@ -586,15 +621,43 @@ 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; + NetdevSocketOptions *stored; + +- if (parse_host_port(&saddr, host_str, errp) < 0) { +- return -1; ++#ifndef WIN32 ++ if (strchr(host_str, ':')) { ++#endif ++ if (parse_host_port((struct sockaddr_in *)&saddr, host_str, errp) < 0) ++ return -1; ++ ++ pf = PF_INET; ++ saddr_size = sizeof(struct sockaddr_in); ++#ifndef WIN32 ++ } else { ++ struct sockaddr_un *saddr_un = (struct sockaddr_un *)&saddr; ++ 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(struct sockaddr_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; +@@ -603,7 +666,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 */ +diff --git a/qemu-options.hx b/qemu-options.hx +index fe83ea09b2..b41351a54e 100644 +--- a/qemu-options.hx ++++ b/qemu-options.hx +@@ -2836,13 +2836,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.28.0 + diff --git a/qemu/0002-net-Don-t-ignore-EINVAL-on-netdev-socket-connection.patch b/qemu/0002-net-Don-t-ignore-EINVAL-on-netdev-socket-connection.patch new file mode 100644 index 0000000..0e2caa8 --- /dev/null +++ b/qemu/0002-net-Don-t-ignore-EINVAL-on-netdev-socket-connection.patch @@ -0,0 +1,31 @@ +From f4c9af4041754209eaad4f98041466b6f5e54902 Mon Sep 17 00:00:00 2001 +From: Stefano Brivio +Date: Wed, 17 Mar 2021 10:35:32 +0100 +Subject: [PATCH 2/2] net: Don't ignore EINVAL on netdev socket connection + +Other errors are treated as failure by net_socket_connect_init(), +but if connect() returns EINVAL, we'll fail silently. Remove the +related exception. + +Signed-off-by: Stefano Brivio +--- + net/socket.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/net/socket.c b/net/socket.c +index 9d06953983..a7c10ea2b2 100644 +--- a/net/socket.c ++++ b/net/socket.c +@@ -671,8 +671,7 @@ static int net_socket_connect_init(NetClientState *peer, + if (errno == EINTR || errno == EWOULDBLOCK) { + /* continue */ + } else if (errno == EINPROGRESS || +- errno == EALREADY || +- errno == EINVAL) { ++ errno == EALREADY) { + break; + } else { + error_setg_errno(errp, errno, "can't connect socket"); +-- +2.28.0 +