Blame SOURCES/kvm-sockets-ensure-we-can-bind-to-both-ipv4-ipv6-separat.patch

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