|
|
76daa3 |
From 469e40bd297c29f28c8ff84105c8c2ea68b6d2b1 Mon Sep 17 00:00:00 2001
|
|
|
76daa3 |
From: "Daniel P. Berrange" <berrange@redhat.com>
|
|
|
76daa3 |
Date: Thu, 22 Jun 2017 12:07:21 +0200
|
|
|
76daa3 |
Subject: [PATCH] sockets: ensure we can bind to both ipv4 & ipv6 separately
|
|
|
76daa3 |
MIME-Version: 1.0
|
|
|
76daa3 |
Content-Type: text/plain; charset=UTF-8
|
|
|
76daa3 |
Content-Transfer-Encoding: 8bit
|
|
|
76daa3 |
|
|
|
76daa3 |
RH-Author: Daniel P. Berrange <berrange@redhat.com>
|
|
|
76daa3 |
Message-id: <20170622120721.7553-1-berrange@redhat.com>
|
|
|
76daa3 |
Patchwork-id: 75672
|
|
|
76daa3 |
O-Subject: [RHEL7.4 qemu-kvm-rhev PATCH v2] sockets: ensure we can bind to both ipv4 & ipv6 separately
|
|
|
76daa3 |
Bugzilla: 1446003
|
|
|
76daa3 |
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
|
|
|
76daa3 |
RH-Acked-by: Markus Armbruster <armbru@redhat.com>
|
|
|
76daa3 |
RH-Acked-by: Laszlo Ersek <lersek@redhat.com>
|
|
|
76daa3 |
|
|
|
76daa3 |
When binding to an IPv6 socket we currently force the
|
|
|
76daa3 |
IPV6_V6ONLY flag to off. This means that the IPv6 socket
|
|
|
76daa3 |
will accept both IPv4 & IPv6 sockets when QEMU is launched
|
|
|
76daa3 |
with something like
|
|
|
76daa3 |
|
|
|
76daa3 |
-vnc :::1
|
|
|
76daa3 |
|
|
|
76daa3 |
While this is good for that case, it is bad for other
|
|
|
76daa3 |
cases. For example if an empty hostname is given,
|
|
|
76daa3 |
getaddrinfo resolves it to 2 addresses 0.0.0.0 and ::,
|
|
|
76daa3 |
in that order. We will thus bind to 0.0.0.0 first, and
|
|
|
76daa3 |
then fail to bind to :: on the same port. The same
|
|
|
76daa3 |
problem can happen if any other hostname lookup causes
|
|
|
76daa3 |
the IPv4 address to be reported before the IPv6 address.
|
|
|
76daa3 |
|
|
|
76daa3 |
When we get an IPv6 bind failure, we should re-try the
|
|
|
76daa3 |
same port, but with IPV6_V6ONLY turned on again, to
|
|
|
76daa3 |
avoid clash with any IPv4 listener.
|
|
|
76daa3 |
|
|
|
76daa3 |
This ensures that
|
|
|
76daa3 |
|
|
|
76daa3 |
-vnc :1
|
|
|
76daa3 |
|
|
|
76daa3 |
will bind successfully to both 0.0.0.0 and ::, and also
|
|
|
76daa3 |
avoid
|
|
|
76daa3 |
|
|
|
76daa3 |
-vnc :1,to=2
|
|
|
76daa3 |
|
|
|
76daa3 |
from mistakenly using a 2nd port for the :: listener.
|
|
|
76daa3 |
|
|
|
76daa3 |
This is a regression due to commit 396f935 "ui: add ability to
|
|
|
76daa3 |
specify multiple VNC listen addresses".
|
|
|
76daa3 |
|
|
|
76daa3 |
Acked-by: Gerd Hoffmann <kraxel@gmail.com>
|
|
|
76daa3 |
Reviewed-by: Eric Blake <eblake@redhat.com>
|
|
|
76daa3 |
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
|
|
|
76daa3 |
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
|
|
|
76daa3 |
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
|
|
|
76daa3 |
---
|
|
|
76daa3 |
util/qemu-sockets.c | 31 +++++++++++++++++++++++--------
|
|
|
76daa3 |
1 file changed, 23 insertions(+), 8 deletions(-)
|
|
|
76daa3 |
|
|
|
76daa3 |
diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c
|
|
|
76daa3 |
index 21442c30..11b5c6f 100644
|
|
|
76daa3 |
--- a/util/qemu-sockets.c
|
|
|
76daa3 |
+++ b/util/qemu-sockets.c
|
|
|
76daa3 |
@@ -207,22 +207,37 @@ static int inet_listen_saddr(InetSocketAddress *saddr,
|
|
|
76daa3 |
}
|
|
|
76daa3 |
|
|
|
76daa3 |
socket_set_fast_reuse(slisten);
|
|
|
76daa3 |
-#ifdef IPV6_V6ONLY
|
|
|
76daa3 |
- if (e->ai_family == PF_INET6) {
|
|
|
76daa3 |
- /* listen on both ipv4 and ipv6 */
|
|
|
76daa3 |
- const int off = 0;
|
|
|
76daa3 |
- qemu_setsockopt(slisten, IPPROTO_IPV6, IPV6_V6ONLY, &off,
|
|
|
76daa3 |
- sizeof(off));
|
|
|
76daa3 |
- }
|
|
|
76daa3 |
-#endif
|
|
|
76daa3 |
|
|
|
76daa3 |
port_min = inet_getport(e);
|
|
|
76daa3 |
port_max = saddr->has_to ? saddr->to + port_offset : port_min;
|
|
|
76daa3 |
for (p = port_min; p <= port_max; p++) {
|
|
|
76daa3 |
+#ifdef IPV6_V6ONLY
|
|
|
76daa3 |
+ /* listen on both ipv4 and ipv6 */
|
|
|
76daa3 |
+ int v6only = 0;
|
|
|
76daa3 |
+#endif
|
|
|
76daa3 |
inet_setport(e, p);
|
|
|
76daa3 |
+#ifdef IPV6_V6ONLY
|
|
|
76daa3 |
+ rebind:
|
|
|
76daa3 |
+ if (e->ai_family == PF_INET6) {
|
|
|
76daa3 |
+ qemu_setsockopt(slisten, IPPROTO_IPV6, IPV6_V6ONLY, &v6only,
|
|
|
76daa3 |
+ sizeof(v6only));
|
|
|
76daa3 |
+ }
|
|
|
76daa3 |
+#endif
|
|
|
76daa3 |
if (bind(slisten, e->ai_addr, e->ai_addrlen) == 0) {
|
|
|
76daa3 |
goto listen;
|
|
|
76daa3 |
}
|
|
|
76daa3 |
+
|
|
|
76daa3 |
+#ifdef IPV6_V6ONLY
|
|
|
76daa3 |
+ /* If we got EADDRINUSE from an IPv6 bind & V6ONLY is unset,
|
|
|
76daa3 |
+ * it could be that the IPv4 port is already claimed, so retry
|
|
|
76daa3 |
+ * with V6ONLY set
|
|
|
76daa3 |
+ */
|
|
|
76daa3 |
+ if (e->ai_family == PF_INET6 && errno == EADDRINUSE && !v6only) {
|
|
|
76daa3 |
+ v6only = 1;
|
|
|
76daa3 |
+ goto rebind;
|
|
|
76daa3 |
+ }
|
|
|
76daa3 |
+#endif
|
|
|
76daa3 |
+
|
|
|
76daa3 |
if (p == port_max) {
|
|
|
76daa3 |
if (!e->ai_next) {
|
|
|
76daa3 |
error_setg_errno(errp, errno, "Failed to bind socket");
|
|
|
76daa3 |
--
|
|
|
76daa3 |
1.8.3.1
|
|
|
76daa3 |
|