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.
Signed-off-by: Daniel P. Berrangé <berrange(a)redhat.com>
---
src/rpc/virnetsocket.c | 28 +++++++++++++++++++++++++++-
1 file changed, 27 insertions(+), 1 deletion(-)
diff --git a/src/rpc/virnetsocket.c b/src/rpc/virnetsocket.c
index 254f39c4ec..fc13b1654a 100644
--- a/src/rpc/virnetsocket.c
+++ b/src/rpc/virnetsocket.c
@@ -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 (runp->ai_addr->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)
--
2.21.0