[PATCH] src/net_socket.c: Bind outgoing TCP sockets to `BindToAddress'.

Florian Forster octo at verplant.org
Wed May 27 09:49:11 CEST 2009


If a host has multiple addresses on an interface, the source address of the TCP
connection(s) was picked by the operating system while the UDP packets used a
bound socket, i. e. the source address was the address specified by the user.
This caused problems because the receiving code requires the TCP connection and
the UDP connection to originate from the same IP address.

This patch adds support for the `BindToInterface' and `BindToAddress' options
to the setup of outgoing TCP connections.

Tested with Debian Etch on x86 and Debian Lenny on x86_64.

Signed-off-by: Florian Forster <octo at verplant.org>
---
 src/net_socket.c |  121 ++++++++++++++++++++++++++++++++++++++++++++++--------
 1 files changed, 104 insertions(+), 17 deletions(-)

diff --git a/src/net_socket.c b/src/net_socket.c
index dcdcc0b..758d36b 100644
--- a/src/net_socket.c
+++ b/src/net_socket.c
@@ -34,6 +34,8 @@
 #include "utils.h"
 #include "xalloc.h"
 
+#include <assert.h>
+
 #ifdef WSAEINPROGRESS
 #define EINPROGRESS WSAEINPROGRESS
 #endif
@@ -82,6 +84,101 @@ static void configure_tcp(connection_t *c)
 #endif
 }
 
+static int bind_to_interface (int sd) /* {{{ */
+{
+	char *iface;
+
+#if defined(SOL_SOCKET) && defined(SO_BINDTODEVICE)
+	struct ifreq ifr;
+	int status;
+#endif /* defined(SOL_SOCKET) && defined(SO_BINDTODEVICE) */
+
+	if (!get_config_string (lookup_config (config_tree, "BindToInterface"), &iface))
+		return (0);
+
+#if defined(SOL_SOCKET) && defined(SO_BINDTODEVICE)
+	memset(&ifr, 0, sizeof(ifr));
+	strncpy(ifr.ifr_ifrn.ifrn_name, iface, IFNAMSIZ);
+	ifr.ifr_ifrn.ifrn_name[IFNAMSIZ - 1] = 0;
+
+	status = setsockopt (sd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr));
+	if (status != 0)
+	{
+		logger(LOG_ERR, _("Can't bind to interface %s: %s"), iface,
+				strerror(errno));
+		return -1;
+	}
+#else /* if !defined(SOL_SOCKET) || !defined(SO_BINDTODEVICE) */
+	logger(LOG_WARNING, _("%s not supported on this platform"), "BindToInterface");
+#endif
+
+	return (0);
+} /* }}} int bind_to_interface */
+
+static int bind_to_address (connection_t *c) /* {{{ */
+{
+	char *node;
+	struct addrinfo *ai_list;
+	struct addrinfo *ai_ptr;
+	struct addrinfo ai_hints;
+	int status;
+
+	assert (c != NULL);
+	assert (c->socket >= 0);
+
+	node = NULL;
+	if (!get_config_string (lookup_config (config_tree, "BindToAddress"),
+				&node))
+		return (0);
+
+	assert (node != NULL);
+
+	memset (&ai_hints, 0, sizeof (ai_hints));
+	ai_hints.ai_family = c->address.sa.sa_family;
+	/* We're called from `do_outgoing_connection' only. */
+	ai_hints.ai_socktype = SOCK_STREAM;
+	ai_hints.ai_protocol = IPPROTO_TCP;
+
+	ai_list = NULL;
+
+	status = getaddrinfo (node, /* service = */ NULL,
+			&ai_hints, &ai_list);
+	if (status != 0)
+	{
+		free (node);
+		logger (LOG_WARNING, _("Error looking up %s port %s: %s"),
+				node, _("any"), gai_strerror(status));
+		return (-1);
+	}
+	assert (ai_list != NULL);
+
+	status = -1;
+	for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
+	{
+		status = bind (c->socket,
+				ai_list->ai_addr, ai_list->ai_addrlen);
+		if (status == 0)
+			break;
+	}
+
+
+	if (status != 0)
+	{
+		logger(LOG_ERR, _("Can't bind to %s/tcp: %s"), node,
+				strerror(errno));
+	}
+	else
+	{
+		logger (LOG_DEBUG, "Successfully bound outgoing "
+				"TCP socket to %s", node);
+	}
+
+	free (node);
+	freeaddrinfo (ai_list);
+
+	return ((status == 0) ? 0 : -1);
+} /* }}} int bind_to_address */
+
 int setup_listen_socket(const sockaddr_t *sa)
 {
 	int nfd;
@@ -206,24 +303,11 @@ int setup_vpn_in_socket(const sockaddr_t *sa)
 	}
 #endif
 
-#if defined(SOL_SOCKET) && defined(SO_BINDTODEVICE)
+	if (bind_to_interface (nfd) != 0)
 	{
-		char *iface;
-		struct ifreq ifr;
-
-		if(get_config_string(lookup_config(config_tree, "BindToInterface"), &iface)) {
-			memset(&ifr, 0, sizeof(ifr));
-			strncpy(ifr.ifr_ifrn.ifrn_name, iface, IFNAMSIZ);
-
-			if(setsockopt(nfd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr))) {
-				closesocket(nfd);
-				logger(LOG_ERR, _("Can't bind to interface %s: %s"), iface,
-					   strerror(errno));
-				return -1;
-			}
-		}
+		closesocket (nfd);
+		return -1;
 	}
-#endif
 
 	if(bind(nfd, &sa->sa, SALEN(sa->sa))) {
 		closesocket(nfd);
@@ -235,7 +319,7 @@ int setup_vpn_in_socket(const sockaddr_t *sa)
 	}
 
 	return nfd;
-}
+} /* int setup_vpn_in_socket */
 
 void retry_outgoing(outgoing_t *outgoing)
 {
@@ -335,6 +419,9 @@ begin:
 		setsockopt(c->socket, SOL_IPV6, IPV6_V6ONLY, &option, sizeof option);
 #endif
 
+	bind_to_interface (c->socket);
+	bind_to_address (c);
+
 	/* Optimize TCP settings */
 
 	configure_tcp(c);
-- 
1.5.6.5



More information about the tinc-devel mailing list