From 23266fe781ce41a167a0a2f312693a0ca6fedd0d Mon Sep 17 00:00:00 2001 From: Daniel Saunders Date: Mon, 3 Jul 2017 15:56:02 -0600 Subject: [PATCH] TRQ-4072. Add bind() before client-side connect() calls --- configure.ac | 12 ++++++ src/lib/Libifl/tm.c | 26 +++++++++++++ src/lib/Libifl/trq_auth.c | 26 +++++++++++++ src/lib/Libnet/lib_net.h | 1 + src/lib/Libnet/net_common.c | 64 +++++++++++++++++++++++++++++--- src/lib/Libnet/port_forwarding.c | 25 +++++++++++++ src/lib/Libnet/rm.c | 27 ++++++++++++-- src/resmom/start_exec.c | 31 ++++++++++++++++ src/test/net_common/test_uut.c | 26 +++++++++++++ 9 files changed, 229 insertions(+), 9 deletions(-) diff --git a/configure.ac b/configure.ac index 82934fd74f..b2a31722bd 100644 --- a/configure.ac +++ b/configure.ac @@ -1858,6 +1858,18 @@ esac +dnl Make a bind() call before making a connect() call +AC_MSG_CHECKING([whether to build with bind() calls before connect() calls]) +AC_ARG_ENABLE(bind-outbound-sockets, + [ --enable-bind-outbound-sockets enable build with bind() calls before connect() calls], + BIND_OUTBOUND_SOCKETS=$enableval,BIND_OUTBOUND_SOCKETS=yes) +AC_MSG_RESULT($BIND_OUTBOUND_SOCKETS) +if test "$BIND_OUTBOUND_SOCKETS" = "yes" ; then + AC_DEFINE(BIND_OUTBOUND_SOCKETS, 1, [turns on the compilation of BIND_OUTBOUND_SOCKETS code]) +fi + + + dnl dnl ###################################################################### dnl Scheduler settings diff --git a/src/lib/Libifl/tm.c b/src/lib/Libifl/tm.c index a08cf384bb..d12a8d79ce 100644 --- a/src/lib/Libifl/tm.c +++ b/src/lib/Libifl/tm.c @@ -484,6 +484,11 @@ static int localmom(void) struct linger ltime; +#ifdef BIND_OUTBOUND_SOCKETS + struct sockaddr_in local; +#endif + + if (local_conn >= 0) { return(local_conn); /* already have open connection */ @@ -528,6 +533,27 @@ static int localmom(void) #endif +#ifdef BIND_OUTBOUND_SOCKETS + + /* Bind to the IP address associated with the hostname, in case there are + * muliple possible source IPs for this destination.*/ + + if (get_local_address(local) != 0) + { + TM_DBPRT(("tm_init: unable to determine local IP address")); + close(sock); + return(-1); + } + + if (bind(sock, (struct sockaddr *)&local, sizeof(sockaddr_in)) != 0) + { + TM_DBPRT(("tm_init: unable to bind to local IP address")); + close(sock); + return(-1); + } +#endif + + /* make sure data goes out */ ltime.l_onoff = 1; diff --git a/src/lib/Libifl/trq_auth.c b/src/lib/Libifl/trq_auth.c index 5e0fced2c5..524b14c404 100644 --- a/src/lib/Libifl/trq_auth.c +++ b/src/lib/Libifl/trq_auth.c @@ -331,6 +331,19 @@ int trq_simple_connect( int optval = 1; std::string server(server_name); +#ifdef BIND_OUTBOUND_SOCKETS + struct sockaddr_in local; + + /* Bind to the IP address associated with the hostname, in case there are + * muliple possible source IPs for this destination.*/ + if (get_local_address(local) != PBSE_NONE) + { + fprintf(stderr, "could not determine local IP address: %s", strerror(errno)); + return(PBSE_SYSTEM); + } + +#endif + memset(&hints, 0, sizeof(hints)); /* set the hints so we get a STREAM socket */ hints.ai_family = AF_UNSPEC; /* allow for IPv4 or IPv6 */ @@ -365,6 +378,19 @@ int trq_simple_connect( continue; } +#ifdef BIND_OUTBOUND_SOCKETS + + rc = bind(sock, (struct sockaddr *)&local, sizeof(sockaddr_in)); + if (rc != 0) + { + fprintf(stderr, "could not bind local socket: %s", strerror(errno)); + rc = PBSE_SYSTEM; + close(sock); + continue; + } + +#endif + rc = connect(sock, addr_info->ai_addr, addr_info->ai_addrlen); if (rc != 0) { diff --git a/src/lib/Libnet/lib_net.h b/src/lib/Libnet/lib_net.h index 6b5929c7ea..4e008ecfb8 100644 --- a/src/lib/Libnet/lib_net.h +++ b/src/lib/Libnet/lib_net.h @@ -52,6 +52,7 @@ int socket_read_str(int socket, char **the_str, long long *str_len); int socket_close(int socket); int pbs_getaddrinfo(const char *pNode,struct addrinfo *pHints,struct addrinfo **ppAddrInfoOut); int connect_to_trqauthd(int *sock); +int get_local_address(struct sockaddr_in &new_sockaddr); /* from file server_core.c */ diff --git a/src/lib/Libnet/net_common.c b/src/lib/Libnet/net_common.c index c6006f61c6..dc4e5702d4 100644 --- a/src/lib/Libnet/net_common.c +++ b/src/lib/Libnet/net_common.c @@ -205,13 +205,22 @@ int socket_get_tcp_priv() int priv_port = 0; int flags; #endif - - memset(&local, 0, sizeof(struct sockaddr_in)); - local.sin_family = AF_INET; - + if ((local_socket = socket_get_tcp()) < 0) return(-1); +#ifdef BIND_OUTBOUND_SOCKETS + if (get_local_address(local) != PBSE_NONE) + { + close(local_socket); + return(-1); + } +#else + memset(&local, 0, sizeof(struct sockaddr_in)); + local.sin_family = AF_INET; +#endif + + #ifndef NOPRIVPORTS /* According to the notes in the previous code: * bindresvport seems to cause connect() failures in some odd corner case @@ -1030,4 +1039,49 @@ int connect_to_trqauthd( return(rc); } - + +/* + * get_local_address() - lookup the local IP address associated with the local, + * public hostname. + * + * This function will cache the IP address in each process to speed up + * frequent lookups. + */ +int get_local_address( + + struct sockaddr_in &new_sockaddr) + + { + static struct addrinfo *local_addr_info = NULL; + struct addrinfo hints; + int rc; + char hostname[PBS_MAXHOSTNAME]; + hostname[PBS_MAXHOSTNAME - 1] = '\0'; + + // If we already have the local address cached, return (copy) that value. + if (local_addr_info != NULL) + { + new_sockaddr = *((sockaddr_in*)local_addr_info->ai_addr); + + return PBSE_NONE; + } + + /* Bind to the IP address associated with the hostname, in case there are + * muliple possible source IPs for this destination.*/ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + + gethostname(hostname, sizeof(hostname)); + + rc = getaddrinfo(hostname, NULL, &hints, &local_addr_info); + if (rc != 0) + { + return(rc); + } + + new_sockaddr = *((sockaddr_in*)local_addr_info->ai_addr); + + return PBSE_NONE; + } + diff --git a/src/lib/Libnet/port_forwarding.c b/src/lib/Libnet/port_forwarding.c index 7569df5084..019618ec37 100644 --- a/src/lib/Libnet/port_forwarding.c +++ b/src/lib/Libnet/port_forwarding.c @@ -18,6 +18,7 @@ #include #include #include "lib_ifl.h" +#include "lib_net.h" #include "port_forwarding.h" @@ -335,6 +336,9 @@ int x11_connect_display( char buf[1024], *cp; struct addrinfo hints, *ai, *aitop; +#ifdef BIND_OUTBOUND_SOCKETS + struct sockaddr_in local; +#endif char strport[NI_MAXSERV]; @@ -413,6 +417,14 @@ int x11_connect_display( return -1; } +#ifdef BIND_OUTBOUND_SOCKETS + if (get_local_address(local) != PBSE_NONE) + { + fprintf(stderr, "could not determine local IP address: %s", strerror(errno)); + return(-1); + } +#endif + for (ai = aitop; ai; ai = ai->ai_next) { /* Create a socket. */ @@ -424,6 +436,19 @@ int x11_connect_display( continue; } +#ifdef BIND_OUTBOUND_SOCKETS + /* Bind to the IP address associated with the hostname, in case there are + * muliple possible source IPs for this destination.*/ + + if (bind(sock, (struct sockaddr *)&local, sizeof(sockaddr_in))) + { + fprintf(stderr, "could not bind local socket: %s", strerror(errno)); + close(sock); + continue; + } + +#endif + /* Connect it to the display. */ if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0) { diff --git a/src/lib/Libnet/rm.c b/src/lib/Libnet/rm.c index b34e78682a..79551b9761 100644 --- a/src/lib/Libnet/rm.c +++ b/src/lib/Libnet/rm.c @@ -207,6 +207,7 @@ int openrm( struct sockaddr_in addr; struct addrinfo *addr_info; + struct sockaddr_in local; if (pbs_getaddrinfo(host, NULL, &addr_info) != 0) { @@ -216,18 +217,36 @@ int openrm( return(ENOENT * -1); } - memset(&addr, '\0', sizeof(addr)); +#ifdef BIND_OUTBOUND_SOCKETS + /* Bind to the IP address associated with the hostname, in case there are + * muliple possible source IPs for this destination.*/ + if (get_local_address(local) != PBSE_NONE) + { + DBPRT(("could not determine local IP address: %s", strerror(errno))); + close(stream); + return(-1); + } - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = htonl(INADDR_ANY); +#else + + memset(&local, 0, sizeof(struct sockaddr_in)); + + local.sin_family = AF_INET; + +#endif while (retries++ < MAX_RETRIES) { - rc = bindresvport(stream, &addr); + /* Attempt to bind to any reserved port that is free. If BIND_OUTBOUND_SOCKETS + * is defined, we need to make sure the socket is bound to a specific + * local IP address - passed in using the `local` structure. + */ + rc = bindresvport(stream, &local); if (rc != 0) { if (retries >= MAX_RETRIES) { + DBPRT(("could not bind local socket: %s", strerror(errno))); close(stream); return(-1*errno); } diff --git a/src/resmom/start_exec.c b/src/resmom/start_exec.c index a286594831..988a3b3c8b 100644 --- a/src/resmom/start_exec.c +++ b/src/resmom/start_exec.c @@ -100,6 +100,9 @@ extern "C" #include #include #include +#include +#include +#include #if IBM_SP2==2 /* IBM SP with PSSP 3.1 */ #include #endif /* IBM SP */ @@ -804,6 +807,10 @@ int open_demux( torque_socklen_t slen; unsigned short local_port; +#ifdef BIND_OUTBOUND_SOCKETS + struct sockaddr_in local; +#endif + memset(&remote, 0, sizeof(remote)); remote.sin_addr.s_addr = addr; remote.sin_port = htons((unsigned short)port); @@ -820,6 +827,30 @@ int open_demux( return(-1); } +#ifdef BIND_OUTBOUND_SOCKETS + /* Bind to the IP address associated with the hostname, in case there are + * muliple possible source IPs for this destination.*/ + + if (get_local_address(local) != PBSE_NONE) + { + sprintf(log_buffer, "could not determine local IP address: %s", strerror(errno)); + log_err(errno, __func__, log_buffer); + + close(sock); + return(-1); + } + + if (bind(sock, (struct sockaddr *)&local, sizeof(local))) + { + sprintf(log_buffer, "could not bind local socket: %s", strerror(errno)); + log_err(errno, __func__, log_buffer); + + close(sock); + return(-1); + } + +#endif + for (i = 0;i < RETRY;i++) { if (connect(sock, (struct sockaddr *)&remote, sizeof(remote)) == 0) diff --git a/src/test/net_common/test_uut.c b/src/test/net_common/test_uut.c index e5209d7087..0cb7686269 100644 --- a/src/test/net_common/test_uut.c +++ b/src/test/net_common/test_uut.c @@ -5,6 +5,9 @@ #include #include #include +#include +#include +#include #include "pbs_error.h" #include "net_cache.h" @@ -144,6 +147,25 @@ START_TEST(test_socket_wait_for_write) } END_TEST +START_TEST(test_get_local_address) + { + int rc; + struct sockaddr_in new_sockaddr; + char hostname[NI_MAXHOST]; + char hbuf[NI_MAXHOST]; + + gethostname(hostname, sizeof(hostname)); + + rc = get_local_address(new_sockaddr); + fail_unless(rc == PBSE_NONE); + + rc = getnameinfo((struct sockaddr*)&new_sockaddr, sizeof(new_sockaddr), hbuf, sizeof(hbuf), NULL, 0, 0); + + fail_unless(rc == 0); + fail_unless(strncmp(hostname, hbuf, NI_MAXHOST) == 0); + } +END_TEST + Suite *net_common_suite(void) { Suite *s = suite_create("net_common_suite methods"); @@ -171,6 +193,10 @@ Suite *net_common_suite(void) tcase_add_test(tc_core, test_socket_wait_for_write); suite_add_tcase(s, tc_core); + tc_core = tcase_create("test_get_local_address"); + tcase_add_test(tc_core, test_get_local_address); + suite_add_tcase(s, tc_core); + return s; }