From 43cca04c339ff4e919b3b51a7eac993a3bc3d060 Mon Sep 17 00:00:00 2001 From: Miroslav Lichvar Date: Mon, 4 Aug 2014 15:49:29 +0200 Subject: [PATCH] ntp: reconnect client sockets With separate client sockets, allow the initial connect() to fail (e.g. when the network is not reachable yet) and try to connect later when sending the packet. Also, reconnect the socket when the local address has changed. --- ntp_io.c | 89 +++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 69 insertions(+), 20 deletions(-) diff --git a/ntp_io.c b/ntp_io.c index 312709d..b77fb79 100644 --- a/ntp_io.c +++ b/ntp_io.c @@ -244,6 +244,23 @@ prepare_socket(int family, int port_number, int client_only) /* ================================================== */ +static int +prepare_separate_client_socket(int family) +{ + switch (family) { + case IPADDR_INET4: + return prepare_socket(AF_INET, 0, 1); +#ifdef HAVE_IPV6 + case IPADDR_INET6: + return prepare_socket(AF_INET6, 0, 1); +#endif + default: + return INVALID_SOCK_FD; + } +} + +/* ================================================== */ + static int connect_socket(int sock_fd, NTP_Remote_Address *remote_addr) { @@ -273,7 +290,7 @@ connect_socket(int sock_fd, NTP_Remote_Address *remote_addr) } if (connect(sock_fd, &addr.u, addr_len) < 0) { - LOG(LOGS_ERR, LOGF_NtpIO, "Could not connect NTP socket to %s:%d : %s", + DEBUG_LOG(LOGF_NtpIO, "Could not connect NTP socket to %s:%d : %s", UTI_IPToString(&remote_addr->ip_addr), remote_addr->port, strerror(errno)); return 0; @@ -296,6 +313,39 @@ close_socket(int sock_fd) /* ================================================== */ +static int +reconnect_socket(int sock_fd, NTP_Remote_Address *remote_addr) +{ + int fd; + + assert(separate_client_sockets && sock_fd != INVALID_SOCK_FD && + !NIO_IsServerSocket(sock_fd)); + + DEBUG_LOG(LOGF_NtpIO, "Reconnecting socket %d to %s:%d", sock_fd, + UTI_IPToString(&remote_addr->ip_addr), remote_addr->port); + + /* Get a new client socket */ + fd = prepare_separate_client_socket(remote_addr->ip_addr.family); + if (fd == INVALID_SOCK_FD) + return 0; + + /* Try to connect */ + if (!connect_socket(fd, remote_addr)) { + close_socket(fd); + return 0; + } + + /* Replace the original socket */ + if (dup2(fd, sock_fd) != sock_fd) { + DEBUG_LOG(LOGF_NtpIO, "Could not duplicate socket : %s", strerror(errno)); + return 0; + } + close_socket(fd); + + return 1; +} + +/* ================================================== */ void NIO_Initialise(int family) { @@ -379,28 +429,14 @@ int NIO_GetClientSocket(NTP_Remote_Address *remote_addr) { if (separate_client_sockets) { - int sock_fd; - - switch (remote_addr->ip_addr.family) { - case IPADDR_INET4: - sock_fd = prepare_socket(AF_INET, 0, 1); - break; -#ifdef HAVE_IPV6 - case IPADDR_INET6: - sock_fd = prepare_socket(AF_INET6, 0, 1); - break; -#endif - default: - sock_fd = INVALID_SOCK_FD; - } + int sock_fd = prepare_separate_client_socket(remote_addr->ip_addr.family); if (sock_fd == INVALID_SOCK_FD) return INVALID_SOCK_FD; - if (!connect_socket(sock_fd, remote_addr)) { - close_socket(sock_fd); - return INVALID_SOCK_FD; - } + /* Try to connect the socket, if it fails (e.g. there is no route + to the address yet), we'll try again later */ + connect_socket(sock_fd, remote_addr); return sock_fd; } else { @@ -578,7 +614,7 @@ send_packet(void *packet, int packetlen, NTP_Remote_Address *remote_addr, NTP_Lo struct msghdr msg; struct iovec iov; char cmsgbuf[256]; - int cmsglen; + int cmsglen, reconnect; socklen_t addrlen = 0; assert(initialised); @@ -671,6 +707,9 @@ send_packet(void *packet, int packetlen, NTP_Remote_Address *remote_addr, NTP_Lo } #endif + reconnect = 0; + +try_again: DEBUG_LOG(LOGF_NtpIO, "Sending to %s:%d from %s fd %d", UTI_IPToString(&remote_addr->ip_addr), remote_addr->port, UTI_IPToString(&local_addr->ip_addr), local_addr->sock_fd); @@ -681,8 +720,18 @@ send_packet(void *packet, int packetlen, NTP_Remote_Address *remote_addr, NTP_Lo msg.msg_control = NULL; if (sendmsg(local_addr->sock_fd, &msg, 0) < 0) { + /* If this is a separate client socket, try to reconnect it (but no more + than once) if not connected yet or if it may be bound to an address that + is no longer valid */ + reconnect = !reconnect && separate_client_sockets && !addrlen && + (errno == ENOTCONN || errno == EDESTADDRREQ || errno == EINVAL); + DEBUG_LOG(LOGF_NtpIO, "Could not send to %s:%d : %s", UTI_IPToString(&remote_addr->ip_addr), remote_addr->port, strerror(errno)); + + /* Try sending the packet again if reconnect succeeds */ + if (reconnect && reconnect_socket(local_addr->sock_fd, remote_addr)) + goto try_again; } }