Blame 0234-Fix-address-handling-in-inet_nonblocking_connect.patch

5544c1
From b3db72271ead92daae43b1534fdfdbe750555b0a Mon Sep 17 00:00:00 2001
5544c1
From: Orit Wasserman <owasserm@redhat.com>
5544c1
Date: Mon, 24 Sep 2012 13:11:09 +0200
5544c1
Subject: [PATCH] Fix address handling in inet_nonblocking_connect
5544c1
5544c1
getaddrinfo can give us a list of addresses, but we only try to
5544c1
connect to the first one. If that fails we never proceed to
5544c1
the next one.  This is common on desktop setups that often have ipv6
5544c1
configured but not actually working.
5544c1
5544c1
To fix this make inet_connect_nonblocking retry connection with a different
5544c1
address.
5544c1
callers on inet_nonblocking_connect register a callback function that will
5544c1
be called when connect opertion completes, in case of failure the fd will have
5544c1
a negative value
5544c1
5544c1
Signed-off-by: Orit Wasserman <owasserm@redhat.com>
5544c1
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
5544c1
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
5544c1
(cherry picked from commit 233aa5c2d1cf4655ffe335025a68cf5454f87dad)
5544c1
5544c1
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
5544c1
---
5544c1
 migration-tcp.c |  37 ++++------------
5544c1
 qemu-char.c     |   2 +-
5544c1
 qemu-sockets.c  | 129 +++++++++++++++++++++++++++++++++++++++++++++-----------
5544c1
 qemu_socket.h   |  16 ++++---
5544c1
 4 files changed, 126 insertions(+), 58 deletions(-)
5544c1
5544c1
diff --git a/migration-tcp.c b/migration-tcp.c
5544c1
index 7f6ad98..a15c2b8 100644
5544c1
--- a/migration-tcp.c
5544c1
+++ b/migration-tcp.c
5544c1
@@ -53,54 +53,35 @@ static int tcp_close(MigrationState *s)
5544c1
     return r;
5544c1
 }
5544c1
 
5544c1
-static void tcp_wait_for_connect(void *opaque)
5544c1
+static void tcp_wait_for_connect(int fd, void *opaque)
5544c1
 {
5544c1
     MigrationState *s = opaque;
5544c1
-    int val, ret;
5544c1
-    socklen_t valsize = sizeof(val);
5544c1
 
5544c1
-    DPRINTF("connect completed\n");
5544c1
-    do {
5544c1
-        ret = getsockopt(s->fd, SOL_SOCKET, SO_ERROR, (void *) &val, &valsize);
5544c1
-    } while (ret == -1 && (socket_error()) == EINTR);
5544c1
-
5544c1
-    if (ret < 0) {
5544c1
+    if (fd < 0) {
5544c1
+        DPRINTF("migrate connect error\n");
5544c1
+        s->fd = -1;
5544c1
         migrate_fd_error(s);
5544c1
-        return;
5544c1
-    }
5544c1
-
5544c1
-    qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL);
5544c1
-
5544c1
-    if (val == 0)
5544c1
+    } else {
5544c1
+        DPRINTF("migrate connect success\n");
5544c1
+        s->fd = fd;
5544c1
         migrate_fd_connect(s);
5544c1
-    else {
5544c1
-        DPRINTF("error connecting %d\n", val);
5544c1
-        migrate_fd_error(s);
5544c1
     }
5544c1
 }
5544c1
 
5544c1
 int tcp_start_outgoing_migration(MigrationState *s, const char *host_port,
5544c1
                                  Error **errp)
5544c1
 {
5544c1
-    bool in_progress;
5544c1
-
5544c1
     s->get_error = socket_errno;
5544c1
     s->write = socket_write;
5544c1
     s->close = tcp_close;
5544c1
 
5544c1
-    s->fd = inet_nonblocking_connect(host_port, &in_progress, errp);
5544c1
+    s->fd = inet_nonblocking_connect(host_port, tcp_wait_for_connect, s,
5544c1
+                                     errp);
5544c1
     if (error_is_set(errp)) {
5544c1
         migrate_fd_error(s);
5544c1
         return -1;
5544c1
     }
5544c1
 
5544c1
-    if (in_progress) {
5544c1
-        DPRINTF("connect in progress\n");
5544c1
-        qemu_set_fd_handler2(s->fd, NULL, NULL, tcp_wait_for_connect, s);
5544c1
-    } else {
5544c1
-        migrate_fd_connect(s);
5544c1
-    }
5544c1
-
5544c1
     return 0;
5544c1
 }
5544c1
 
5544c1
diff --git a/qemu-char.c b/qemu-char.c
5544c1
index 13b87b5..b082bae 100644
5544c1
--- a/qemu-char.c
5544c1
+++ b/qemu-char.c
5544c1
@@ -2456,7 +2456,7 @@ static CharDriverState *qemu_chr_open_socket(QemuOpts *opts)
5544c1
         if (is_listen) {
5544c1
             fd = inet_listen_opts(opts, 0, NULL);
5544c1
         } else {
5544c1
-            fd = inet_connect_opts(opts, true, NULL, NULL);
5544c1
+            fd = inet_connect_opts(opts, NULL, NULL, NULL);
5544c1
         }
5544c1
     }
5544c1
     if (fd < 0) {
5544c1
diff --git a/qemu-sockets.c b/qemu-sockets.c
5544c1
index 0883a66..1f14e8b 100644
5544c1
--- a/qemu-sockets.c
5544c1
+++ b/qemu-sockets.c
5544c1
@@ -24,6 +24,7 @@
5544c1
 
5544c1
 #include "qemu_socket.h"
5544c1
 #include "qemu-common.h" /* for qemu_isdigit */
5544c1
+#include "main-loop.h"
5544c1
 
5544c1
 #ifndef AI_ADDRCONFIG
5544c1
 # define AI_ADDRCONFIG 0
5544c1
@@ -214,14 +215,66 @@ listen:
5544c1
     ((rc) == -EINPROGRESS)
5544c1
 #endif
5544c1
 
5544c1
-static int inet_connect_addr(struct addrinfo *addr, bool block,
5544c1
-                             bool *in_progress)
5544c1
+/* Struct to store connect state for non blocking connect */
5544c1
+typedef struct ConnectState {
5544c1
+    int fd;
5544c1
+    struct addrinfo *addr_list;
5544c1
+    struct addrinfo *current_addr;
5544c1
+    NonBlockingConnectHandler *callback;
5544c1
+    void *opaque;
5544c1
+} ConnectState;
5544c1
+
5544c1
+static int inet_connect_addr(struct addrinfo *addr, bool *in_progress,
5544c1
+                             ConnectState *connect_state);
5544c1
+
5544c1
+static void wait_for_connect(void *opaque)
5544c1
 {
5544c1
-    int sock, rc;
5544c1
+    ConnectState *s = opaque;
5544c1
+    int val = 0, rc = 0;
5544c1
+    socklen_t valsize = sizeof(val);
5544c1
+    bool in_progress;
5544c1
+
5544c1
+    qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL);
5544c1
+
5544c1
+    do {
5544c1
+        rc = getsockopt(s->fd, SOL_SOCKET, SO_ERROR, (void *) &val, &valsize);
5544c1
+    } while (rc == -1 && socket_error() == EINTR);
5544c1
+
5544c1
+    /* update rc to contain error */
5544c1
+    if (!rc && val) {
5544c1
+        rc = -1;
5544c1
+    }
5544c1
+
5544c1
+    /* connect error */
5544c1
+    if (rc < 0) {
5544c1
+        closesocket(s->fd);
5544c1
+        s->fd = rc;
5544c1
+    }
5544c1
+
5544c1
+    /* try to connect to the next address on the list */
5544c1
+    while (s->current_addr->ai_next != NULL && s->fd < 0) {
5544c1
+        s->current_addr = s->current_addr->ai_next;
5544c1
+        s->fd = inet_connect_addr(s->current_addr, &in_progress, s);
5544c1
+        /* connect in progress */
5544c1
+        if (in_progress) {
5544c1
+            return;
5544c1
+        }
5544c1
+    }
5544c1
 
5544c1
-    if (in_progress) {
5544c1
-        *in_progress = false;
5544c1
+    freeaddrinfo(s->addr_list);
5544c1
+    if (s->callback) {
5544c1
+        s->callback(s->fd, s->opaque);
5544c1
     }
5544c1
+    g_free(s);
5544c1
+    return;
5544c1
+}
5544c1
+
5544c1
+static int inet_connect_addr(struct addrinfo *addr, bool *in_progress,
5544c1
+                             ConnectState *connect_state)
5544c1
+{
5544c1
+    int sock, rc;
5544c1
+
5544c1
+    *in_progress = false;
5544c1
 
5544c1
     sock = qemu_socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
5544c1
     if (sock < 0) {
5544c1
@@ -230,7 +283,7 @@ static int inet_connect_addr(struct addrinfo *addr, bool block,
5544c1
         return -1;
5544c1
     }
5544c1
     setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
5544c1
-    if (!block) {
5544c1
+    if (connect_state != NULL) {
5544c1
         socket_set_nonblock(sock);
5544c1
     }
5544c1
     /* connect to peer */
5544c1
@@ -241,10 +294,11 @@ static int inet_connect_addr(struct addrinfo *addr, bool block,
5544c1
         }
5544c1
     } while (rc == -EINTR);
5544c1
 
5544c1
-    if (!block && QEMU_SOCKET_RC_INPROGRESS(rc)) {
5544c1
-        if (in_progress) {
5544c1
-            *in_progress = true;
5544c1
-        }
5544c1
+    if (connect_state != NULL && QEMU_SOCKET_RC_INPROGRESS(rc)) {
5544c1
+        connect_state->fd = sock;
5544c1
+        qemu_set_fd_handler2(sock, NULL, NULL, wait_for_connect,
5544c1
+                             connect_state);
5544c1
+        *in_progress = true;
5544c1
     } else if (rc < 0) {
5544c1
         closesocket(sock);
5544c1
         return -1;
5544c1
@@ -260,6 +314,7 @@ static struct addrinfo *inet_parse_connect_opts(QemuOpts *opts, Error **errp)
5544c1
     const char *port;
5544c1
 
5544c1
     memset(&ai, 0, sizeof(ai));
5544c1
+
5544c1
     ai.ai_flags = AI_CANONNAME | AI_ADDRCONFIG;
5544c1
     ai.ai_family = PF_UNSPEC;
5544c1
     ai.ai_socktype = SOCK_STREAM;
5544c1
@@ -296,36 +351,55 @@ static struct addrinfo *inet_parse_connect_opts(QemuOpts *opts, Error **errp)
5544c1
  *
5544c1
  * @opts: QEMU options, recognized parameters strings "host" and "port",
5544c1
  *        bools "ipv4" and "ipv6".
5544c1
- * @block: set true for blocking socket
5544c1
- * @in_progress: set to true in case of ongoing connect
5544c1
  * @errp: set on error
5544c1
+ * @callback: callback function for non-blocking connect
5544c1
+ * @opaque: opaque for callback function
5544c1
  *
5544c1
  * Returns: -1 on error, file descriptor on success.
5544c1
+ *
5544c1
+ * If @callback is non-null, the connect is non-blocking.  If this
5544c1
+ * function succeeds, callback will be called when the connection
5544c1
+ * completes, with the file descriptor on success, or -1 on error.
5544c1
  */
5544c1
-int inet_connect_opts(QemuOpts *opts, bool block, bool *in_progress,
5544c1
-                      Error **errp)
5544c1
+int inet_connect_opts(QemuOpts *opts, Error **errp,
5544c1
+                      NonBlockingConnectHandler *callback, void *opaque)
5544c1
 {
5544c1
     struct addrinfo *res, *e;
5544c1
     int sock = -1;
5544c1
+    bool in_progress;
5544c1
+    ConnectState *connect_state = NULL;
5544c1
 
5544c1
     res = inet_parse_connect_opts(opts, errp);
5544c1
     if (!res) {
5544c1
         return -1;
5544c1
     }
5544c1
 
5544c1
-    if (in_progress) {
5544c1
-        *in_progress = false;
5544c1
+    if (callback != NULL) {
5544c1
+        connect_state = g_malloc0(sizeof(*connect_state));
5544c1
+        connect_state->addr_list = res;
5544c1
+        connect_state->callback = callback;
5544c1
+        connect_state->opaque = opaque;
5544c1
     }
5544c1
 
5544c1
     for (e = res; e != NULL; e = e->ai_next) {
5544c1
-        sock = inet_connect_addr(e, block, in_progress);
5544c1
-        if (sock >= 0) {
5544c1
+        if (connect_state != NULL) {
5544c1
+            connect_state->current_addr = e;
5544c1
+        }
5544c1
+        sock = inet_connect_addr(e, &in_progress, connect_state);
5544c1
+        if (in_progress) {
5544c1
+            return sock;
5544c1
+        } else if (sock >= 0) {
5544c1
+            /* non blocking socket immediate success, call callback */
5544c1
+            if (callback != NULL) {
5544c1
+                callback(sock, opaque);
5544c1
+            }
5544c1
             break;
5544c1
         }
5544c1
     }
5544c1
     if (sock < 0) {
5544c1
         error_set(errp, QERR_SOCKET_CONNECT_FAILED);
5544c1
     }
5544c1
+    g_free(connect_state);
5544c1
     freeaddrinfo(res);
5544c1
     return sock;
5544c1
 }
5544c1
@@ -538,7 +612,7 @@ int inet_connect(const char *str, Error **errp)
5544c1
 
5544c1
     opts = qemu_opts_create(&dummy_opts, NULL, 0, NULL);
5544c1
     if (inet_parse(opts, str) == 0) {
5544c1
-        sock = inet_connect_opts(opts, true, NULL, errp);
5544c1
+        sock = inet_connect_opts(opts, errp, NULL, NULL);
5544c1
     } else {
5544c1
         error_set(errp, QERR_SOCKET_CREATE_FAILED);
5544c1
     }
5544c1
@@ -548,22 +622,29 @@ int inet_connect(const char *str, Error **errp)
5544c1
 
5544c1
 /**
5544c1
  * Create a non-blocking socket and connect it to an address.
5544c1
+ * Calls the callback function with fd in case of success or -1 in case of
5544c1
+ * error.
5544c1
  *
5544c1
  * @str: address string
5544c1
- * @in_progress: set to true in case of ongoing connect
5544c1
+ * @callback: callback function that is called when connect completes,
5544c1
+ *            cannot be NULL.
5544c1
+ * @opaque: opaque for callback function
5544c1
  * @errp: set in case of an error
5544c1
  *
5544c1
- * Returns: -1 on error, file descriptor on success.
5544c1
+ * Returns: -1 on immediate error, file descriptor on success.
5544c1
  **/
5544c1
-int inet_nonblocking_connect(const char *str, bool *in_progress,
5544c1
-                             Error **errp)
5544c1
+int inet_nonblocking_connect(const char *str,
5544c1
+                             NonBlockingConnectHandler *callback,
5544c1
+                             void *opaque, Error **errp)
5544c1
 {
5544c1
     QemuOpts *opts;
5544c1
     int sock = -1;
5544c1
 
5544c1
+    g_assert(callback != NULL);
5544c1
+
5544c1
     opts = qemu_opts_create(&dummy_opts, NULL, 0, NULL);
5544c1
     if (inet_parse(opts, str) == 0) {
5544c1
-        sock = inet_connect_opts(opts, false, in_progress, errp);
5544c1
+        sock = inet_connect_opts(opts, errp, callback, opaque);
5544c1
     } else {
5544c1
         error_set(errp, QERR_SOCKET_CREATE_FAILED);
5544c1
     }
5544c1
diff --git a/qemu_socket.h b/qemu_socket.h
5544c1
index 80696aa..3e8aee9 100644
5544c1
--- a/qemu_socket.h
5544c1
+++ b/qemu_socket.h
5544c1
@@ -38,15 +38,21 @@ void socket_set_block(int fd);
5544c1
 void socket_set_nonblock(int fd);
5544c1
 int send_all(int fd, const void *buf, int len1);
5544c1
 
5544c1
-/* New, ipv6-ready socket helper functions, see qemu-sockets.c */
5544c1
+/* callback function for nonblocking connect
5544c1
+ * valid fd on success, negative error code on failure
5544c1
+ */
5544c1
+typedef void NonBlockingConnectHandler(int fd, void *opaque);
5544c1
+
5544c1
 int inet_listen_opts(QemuOpts *opts, int port_offset, Error **errp);
5544c1
 int inet_listen(const char *str, char *ostr, int olen,
5544c1
                 int socktype, int port_offset, Error **errp);
5544c1
-int inet_connect_opts(QemuOpts *opts, bool block, bool *in_progress,
5544c1
-                      Error **errp);
5544c1
+int inet_connect_opts(QemuOpts *opts, Error **errp,
5544c1
+                      NonBlockingConnectHandler *callback, void *opaque);
5544c1
 int inet_connect(const char *str, Error **errp);
5544c1
-int inet_nonblocking_connect(const char *str, bool *in_progress,
5544c1
-                             Error **errp);
5544c1
+int inet_nonblocking_connect(const char *str,
5544c1
+                             NonBlockingConnectHandler *callback,
5544c1
+                             void *opaque, Error **errp);
5544c1
+
5544c1
 int inet_dgram_opts(QemuOpts *opts);
5544c1
 const char *inet_strfamily(int family);
5544c1
 
5544c1
-- 
5544c1
1.7.12.1
5544c1