]> git.ipfire.org Git - thirdparty/libvirt.git/commitdiff
rpc: ensure all sockets bind to same port when service is NULL
authorDaniel P. Berrangé <berrange@redhat.com>
Wed, 26 Jun 2019 11:22:29 +0000 (12:22 +0100)
committerDaniel P. Berrangé <berrange@redhat.com>
Fri, 12 Jul 2019 15:55:39 +0000 (16:55 +0100)
When the service passed to getaddrinfo is NULL the kernel will choose a
free port to bind to. In a dual stack though we will get separate
sockets for IPv4 and IPv6 and we need them to bind to the same port
number. Thus once the kerel has auto-selected a port for the first
socket, we must disable auto-select for subsequent IP sockets and force
reuse of the first port.

Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
src/rpc/virnetsocket.c

index ed9ae7265355b6b6b1d7e101cb28cf799713e399..2346eb8b0fdfc75c47e0bbca36dcbbf23f8abc5c 100644 (file)
@@ -311,6 +311,7 @@ int virNetSocketNewListenTCP(const char *nodename,
     int socketErrno = 0;
     int bindErrno = 0;
     virSocketAddr tmp_addr;
+    int port = 0;
 
     *retsocks = NULL;
     *nretsocks = 0;
@@ -379,7 +380,24 @@ int virNetSocketNewListenTCP(const char *nodename,
         }
 #endif
 
-        if (bind(fd, runp->ai_addr, runp->ai_addrlen) < 0) {
+        addr.len = runp->ai_addrlen;
+        memcpy(&addr.data.sa, runp->ai_addr, runp->ai_addrlen);
+
+        /* When service is NULL, we let the kernel auto-select the
+         * port. Once we've selected a port for one IP protocol
+         * though, we want to ensure we pick the same port for the
+         * other IP protocol
+         */
+        if (port != 0 && service == NULL) {
+            if (addr.data.sa.sa_family == AF_INET) {
+                addr.data.inet4.sin_port = port;
+            } else if (addr.data.sa.sa_family == AF_INET6) {
+                addr.data.inet6.sin6_port = port;
+            }
+            VIR_DEBUG("Used saved port %d", port);
+        }
+
+        if (bind(fd, &addr.data.sa, addr.len) < 0) {
             if (errno != EADDRINUSE && errno != EADDRNOTAVAIL) {
                 virReportSystemError(errno, "%s", _("Unable to bind to port"));
                 goto error;
@@ -396,6 +414,14 @@ int virNetSocketNewListenTCP(const char *nodename,
             goto error;
         }
 
+        if (port == 0 && service == NULL) {
+            if (addr.data.sa.sa_family == AF_INET)
+                port = addr.data.inet4.sin_port;
+            else if (addr.data.sa.sa_family == AF_INET6)
+                port = addr.data.inet6.sin6_port;
+            VIR_DEBUG("Saved port %d", port);
+        }
+
         VIR_DEBUG("%p f=%d f=%d", &addr, runp->ai_family, addr.data.sa.sa_family);
 
         if (VIR_EXPAND_N(socks, nsocks, 1) < 0)