src/net_socket.c: Bind outgoing TCP sockets to `BindToAddress'.
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@verplant.org>
This commit is contained in:
parent
6b415a1a7f
commit
41a05f59ba
1 changed files with 96 additions and 18 deletions
110
src/net_socket.c
110
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,93 @@ static void configure_tcp(connection_t *c)
|
|||
#endif
|
||||
}
|
||||
|
||||
static bool 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 true;
|
||||
|
||||
#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) {
|
||||
logger(LOG_ERR, _("Can't bind to interface %s: %s"), iface,
|
||||
strerror(errno));
|
||||
return false;
|
||||
}
|
||||
#else /* if !defined(SOL_SOCKET) || !defined(SO_BINDTODEVICE) */
|
||||
logger(LOG_WARNING, _("%s not supported on this platform"), "BindToInterface");
|
||||
#endif
|
||||
|
||||
return true;
|
||||
} /* }}} bool bind_to_interface */
|
||||
|
||||
static bool 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 true;
|
||||
|
||||
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) {
|
||||
free(node);
|
||||
logger(LOG_WARNING, _("Error looking up %s port %s: %s"),
|
||||
node, _("any"), gai_strerror(status));
|
||||
return false;
|
||||
}
|
||||
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)
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if(status) {
|
||||
logger(LOG_ERR, _("Can't bind to %s/tcp: %s"), node,
|
||||
strerror(errno));
|
||||
} else ifdebug(CONNECTIONS) {
|
||||
logger(LOG_DEBUG, "Successfully bound outgoing "
|
||||
"TCP socket to %s", node);
|
||||
}
|
||||
|
||||
free(node);
|
||||
freeaddrinfo(ai_list);
|
||||
|
||||
return status ? false : true;
|
||||
} /* }}} bool bind_to_address */
|
||||
|
||||
int setup_listen_socket(const sockaddr_t *sa)
|
||||
{
|
||||
int nfd;
|
||||
|
@ -206,24 +295,10 @@ int setup_vpn_in_socket(const sockaddr_t *sa)
|
|||
}
|
||||
#endif
|
||||
|
||||
#if defined(SOL_SOCKET) && defined(SO_BINDTODEVICE)
|
||||
{
|
||||
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))) {
|
||||
if (!bind_to_interface(nfd)) {
|
||||
closesocket(nfd);
|
||||
logger(LOG_ERR, _("Can't bind to interface %s: %s"), iface,
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if(bind(nfd, &sa->sa, SALEN(sa->sa))) {
|
||||
closesocket(nfd);
|
||||
|
@ -235,7 +310,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 +410,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);
|
||||
|
|
Loading…
Reference in a new issue