This allows us to use ipv4/ipv6 for migration addresses. Once there, it also uses /etc/services names (it came free).
Signed-off-by: Juan Quintela <quint...@redhat.com> Signed-off-by: Amos Kong <ak...@redhat.com> --- migration-tcp.c | 60 ++++++++----------------------- net.c | 108 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ qemu_socket.h | 3 ++ 3 files changed, 127 insertions(+), 44 deletions(-) diff --git a/migration-tcp.c b/migration-tcp.c index 35a5781..644bb8f 100644 --- a/migration-tcp.c +++ b/migration-tcp.c @@ -81,43 +81,27 @@ static void tcp_wait_for_connect(void *opaque) int tcp_start_outgoing_migration(MigrationState *s, const char *host_port) { - struct sockaddr_in addr; int ret; - - ret = parse_host_port(&addr, host_port); - if (ret < 0) { - return ret; - } + int fd; s->get_error = socket_errno; s->write = socket_write; s->close = tcp_close; - s->fd = qemu_socket(PF_INET, SOCK_STREAM, 0); - if (s->fd == -1) { - DPRINTF("Unable to open socket"); - return -socket_error(); - } - - socket_set_nonblock(s->fd); - - do { - ret = connect(s->fd, (struct sockaddr *)&addr, sizeof(addr)); - if (ret == -1) { - ret = -socket_error(); - } - if (ret == -EINPROGRESS || ret == -EWOULDBLOCK) { - qemu_set_fd_handler2(s->fd, NULL, NULL, tcp_wait_for_connect, s); - return 0; - } - } while (ret == -EINTR); - - if (ret < 0) { + ret = tcp_client_start(host_port, &fd); + s->fd = fd; + if (ret == -EINPROGRESS || ret == -EWOULDBLOCK) { + DPRINTF("connect in progress"); + qemu_set_fd_handler2(s->fd, NULL, NULL, tcp_wait_for_connect, s); + } else if (ret < 0) { DPRINTF("connect failed\n"); - migrate_fd_error(s); + if (ret != -EINVAL) { + migrate_fd_error(s); + } return ret; + } else { + migrate_fd_connect(s); } - migrate_fd_connect(s); return 0; } @@ -157,28 +141,16 @@ out2: int tcp_start_incoming_migration(const char *host_port) { - struct sockaddr_in addr; - int val; + int ret; int s; DPRINTF("Attempting to start an incoming migration\n"); - if (parse_host_port(&addr, host_port) < 0) { - fprintf(stderr, "invalid host/port combination: %s\n", host_port); - return -EINVAL; - } - - s = qemu_socket(PF_INET, SOCK_STREAM, 0); - if (s == -1) { - return -socket_error(); + ret = tcp_server_start(host_port, &s); + if (ret < 0) { + return ret; } - val = 1; - setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *)&val, sizeof(val)); - - if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) == -1) { - goto err; - } if (listen(s, 1) == -1) { goto err; } diff --git a/net.c b/net.c index c34474f..f63014c 100644 --- a/net.c +++ b/net.c @@ -99,6 +99,114 @@ static int get_str_sep(char *buf, int buf_size, const char **pp, int sep) return 0; } +static int tcp_server_bind(int fd, struct addrinfo *rp) +{ + int ret; + int val = 1; + + /* allow fast reuse */ + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&val, sizeof(val)); + + ret = bind(fd, rp->ai_addr, rp->ai_addrlen); + + if (ret == -1) { + ret = -socket_error(); + } + return ret; + +} + +static int tcp_client_connect(int fd, struct addrinfo *rp) +{ + int ret; + + do { + ret = connect(fd, rp->ai_addr, rp->ai_addrlen); + if (ret == -1) { + ret = -socket_error(); + } + } while (ret == -EINTR); + + return ret; +} + + +static int tcp_start_common(const char *str, int *fd, bool server) +{ + char hostname[512]; + const char *service; + const char *name; + struct addrinfo hints; + struct addrinfo *result, *rp; + int s; + int sfd; + int ret = -EINVAL; + + *fd = -1; + service = str; + if (get_str_sep(hostname, sizeof(hostname), &service, ':') < 0) { + return -EINVAL; + } + if (server && strlen(hostname) == 0) { + name = NULL; + } else { + name = hostname; + } + + /* Obtain address(es) matching host/port */ + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ + hints.ai_socktype = SOCK_STREAM; /* Datagram socket */ + + if (server) { + hints.ai_flags = AI_PASSIVE; + } + + s = getaddrinfo(name, service, &hints, &result); + if (s != 0) { + fprintf(stderr, "qemu: getaddrinfo: %s\n", gai_strerror(s)); + return -EINVAL; + } + + /* getaddrinfo() returns a list of address structures. + Try each address until we successfully bind/connect). + If socket(2) (or bind/connect(2)) fails, we (close the socket + and) try the next address. */ + + for (rp = result; rp != NULL; rp = rp->ai_next) { + sfd = qemu_socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (sfd == -1) { + ret = -errno; + continue; + } + socket_set_nonblock(sfd); + if (server) { + ret = tcp_server_bind(sfd, rp); + } else { + ret = tcp_client_connect(sfd, rp); + } + if (ret >= 0 || ret == -EINPROGRESS || ret == -EWOULDBLOCK) { + *fd = sfd; + break; /* Success */ + } + close(sfd); + } + + freeaddrinfo(result); + return ret; +} + +int tcp_server_start(const char *str, int *fd) +{ + return tcp_start_common(str, fd, true); +} + +int tcp_client_start(const char *str, int *fd) +{ + return tcp_start_common(str, fd, false); +} + int parse_host_port(struct sockaddr_in *saddr, const char *str) { char buf[512]; diff --git a/qemu_socket.h b/qemu_socket.h index fe4cf6c..6024496 100644 --- a/qemu_socket.h +++ b/qemu_socket.h @@ -58,4 +58,7 @@ int unix_connect(const char *path); int parse_host_port(struct sockaddr_in *saddr, const char *str); int socket_init(void); +int tcp_client_start(const char *str, int *fd); +int tcp_server_start(const char *str, int *fd); + #endif /* QEMU_SOCKET_H */