Add the ListenAddress option.

ListenAddress works the same as BindToAddress, except that from now on,
explicitly binding outgoing packets to the address of a socket is only done for
sockets specified with BindToAddress.
This commit is contained in:
Guus Sliepen 2014-01-20 21:19:13 +01:00
parent e187758a7e
commit 38adc8bf54
7 changed files with 144 additions and 92 deletions

View file

@ -4,7 +4,7 @@ _tinc() {
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
opts="-c -d -D -K -n -o -L -R -U --config --no-detach --debug --net --option --mlock --logfile --pidfile --chroot --user --help --version"
confvars="Address AddressFamily BindToAddress BindToInterface Broadcast Cipher ClampMSS Compression ConnectTo DecrementTTL Device DeviceType Digest DirectOnly ECDSAPrivateKeyFile ECDSAPublicKey ECDSAPublicKeyFile ExperimentalProtocol Forwarding GraphDumpFile Hostnames IffOneQueue IndirectData Interface KeyExpire LocalDiscovery MACExpire MACLength MaxOutputBufferSize MaxTimeout Mode Name PMTU PMTUDiscovery PingInterval PingTimeout Port PriorityInheritance PrivateKeyFile ProcessPriority Proxy PublicKeyFile ReplayWindow StrictSubnets Subnet TCPOnly TunnelServer UDPRcvBuf UDPSndBuf VDEGroup VDEPort Weight"
confvars="Address AddressFamily BindToAddress BindToInterface Broadcast Cipher ClampMSS Compression ConnectTo DecrementTTL Device DeviceType Digest DirectOnly ECDSAPrivateKeyFile ECDSAPublicKey ECDSAPublicKeyFile ExperimentalProtocol Forwarding GraphDumpFile Hostnames IffOneQueue IndirectData Interface KeyExpire ListenAddress LocalDiscovery MACExpire MACLength MaxOutputBufferSize MaxTimeout Mode Name PMTU PMTUDiscovery PingInterval PingTimeout Port PriorityInheritance PrivateKeyFile ProcessPriority Proxy PublicKeyFile ReplayWindow StrictSubnets Subnet TCPOnly TunnelServer UDPRcvBuf UDPSndBuf VDEGroup VDEPort Weight"
commands="add connect debug del disconnect dump edit export export-all generate-ecdsa-keys generate-keys generate-rsa-keys get help import info init invite join log pcap pid purge reload restart retry set start stop top version"
case ${prev} in

View file

@ -124,23 +124,14 @@ by automatically making or breaking connections to known nodes.
Higher values increase redundancy but also increase meta data overhead.
When using this option, a good value is 3.
.It Va BindToAddress Li = Ar address Op Ar port
If your computer has more than one IPv4 or IPv6 address,
.Nm tinc
will by default listen on all of them for incoming connections.
Multiple
This is the same as
.Va ListenAddress ,
however the address given with the
.Va BindToAddress
variables may be specified,
in which case listening sockets for each specified address are made.
.Pp
If no
.Ar port
is specified, the socket will be bound to the port specified by the
.Va Port
option, or to port 655 if neither is given.
To only bind to a specific port but not to a specific address, use
.Li *
for the
.Ar address .
option will also be used for outgoing connections. This is useful if your
computer has more than one IPv4 or IPv6 address, and you want
.Nm tinc
to only use a specific one for outgoing packets.
.It Va BindToInterface Li = Ar interface Bq experimental
If your computer has more than one network interface,
.Nm tinc
@ -316,6 +307,25 @@ this variable is almost always already correctly set.
This option controls the period the encryption keys used to encrypt the data are valid.
It is common practice to change keys at regular intervals to make it even harder for crackers,
even though it is thought to be nearly impossible to crack a single key.
.It Va ListenAddress Li = Ar address Op Ar port
If your computer has more than one IPv4 or IPv6 address,
.Nm tinc
will by default listen on all of them for incoming connections.
This option can be used to restrict which addresses tinc listens on.
Multiple
.Va ListenAddress
variables may be specified,
in which case listening sockets for each specified address are made.
.Pp
If no
.Ar port
is specified, the socket will listen on the port specified by the
.Va Port
option, or to port 655 if neither is given.
To only listen on a specific port but not on a specific address, use
.Li *
for the
.Ar address .
.It Va LocalDiscovery Li = yes | no Pq no
When enabled,
.Nm tinc
@ -436,10 +446,10 @@ are available.
.It Va ReplayWindow Li = Ar bytes Pq 16
This is the size of the replay tracking window for each remote node, in bytes.
The window is a bitfield which tracks 1 packet per bit, so for example
the default setting of 16 will track up to 128 packets in the window. In high
the default setting of 16 will track up to 128 packets in the window. In high
bandwidth scenarios, setting this to a higher value can reduce packet loss from
the interaction of replay tracking with underlying real packet loss and/or
reordering. Setting this to zero will disable replay tracking completely and
reordering. Setting this to zero will disable replay tracking completely and
pass all traffic, but leaves tinc vulnerable to replay-based attacks on your
traffic.
.It Va StrictSubnets Li = yes | no Po no Pc Bq experimental
@ -562,7 +572,7 @@ IPv6 subnets are notated like fec0:0:0:1::/64.
MAC addresses are notated like 0:1a:2b:3c:4d:5e.
.Pp
A Subnet can be given a weight to indicate its priority over identical Subnets
owned by different nodes. The default weight is 10. Lower values indicate
owned by different nodes. The default weight is 10. Lower values indicate
higher priority. Packets will be sent to the node with the highest priority,
unless that node is not reachable, in which case the node with the next highest
priority will be tried, and so on.

View file

@ -852,14 +852,10 @@ When using this option, a good value is 3.
@cindex BindToAddress
@item BindToAddress = <@var{address}> [<@var{port}>]
If your computer has more than one IPv4 or IPv6 address, tinc
will by default listen on all of them for incoming connections.
Multiple BindToAddress variables may be specified,
in which case listening sockets for each specified address are made.
If no @var{port} is specified, the socket will be bound to the port specified by the Port option,
or to port 655 if neither is given.
To only bind to a specific port but not to a specific address, use "*" for the @var{address}.
This is the same as ListenAddress, however the address given with the BindToAddress option
will also be used for outgoing connections.
This is useful if your computer has more than one IPv4 or IPv6 address,
and you want tinc to only use a specific one for outgoing packets.
@cindex BindToInterface
@item BindToInterface = <@var{interface}> [experimental]
@ -1050,6 +1046,18 @@ Depending on the operating system and the type of device this may or may not act
Under Windows, this variable is used to select which network interface will be used.
If you specified a Device, this variable is almost always already correctly set.
@cindex ListenAddress
@item ListenAddress = <@var{address}> [<@var{port}>]
If your computer has more than one IPv4 or IPv6 address, tinc
will by default listen on all of them for incoming connections.
This option can be used to restrict which addresses tinc listens on.
Multiple ListenAddress variables may be specified,
in which case listening sockets for each specified address are made.
If no @var{port} is specified, the socket will listen on the port specified by the Port option,
or to port 655 if neither is given.
To only listen on a specific port but not to a specific address, use "*" for the @var{address}.
@cindex LocalDiscovery
@item LocalDiscovery = <yes | no> (no)
When enabled, tinc will try to detect peers that are on the same local network.

View file

@ -103,6 +103,7 @@ typedef struct listen_socket_t {
io_t tcp;
io_t udp;
sockaddr_t sa;
bool bindto;
} listen_socket_t;
#include "conf.h"

View file

@ -641,6 +641,85 @@ bool setup_myself_reloadable(void) {
return true;
}
/*
Add listening sockets.
*/
static bool add_listen_address(char *address, bool bindto) {
char *port = myport;
if(address) {
char *space = strchr(address, ' ');
if(space) {
*space++ = 0;
port = space;
}
if(!strcmp(address, "*"))
*address = 0;
}
struct addrinfo *ai, hint = {0};
hint.ai_family = addressfamily;
hint.ai_socktype = SOCK_STREAM;
hint.ai_protocol = IPPROTO_TCP;
hint.ai_flags = AI_PASSIVE;
int err = getaddrinfo(address && *address ? address : NULL, port, &hint, &ai);
free(address);
if(err || !ai) {
logger(DEBUG_ALWAYS, LOG_ERR, "System call `%s' failed: %s", "getaddrinfo", err == EAI_SYSTEM ? strerror(err) : gai_strerror(err));
return false;
}
for(struct addrinfo *aip = ai; aip; aip = aip->ai_next) {
// Ignore duplicate addresses
bool found = false;
for(int i = 0; i < listen_sockets; i++)
if(!memcmp(&listen_socket[i].sa, aip->ai_addr, aip->ai_addrlen)) {
found = true;
break;
}
if(found)
continue;
if(listen_sockets >= MAXSOCKETS) {
logger(DEBUG_ALWAYS, LOG_ERR, "Too many listening sockets");
return false;
}
int tcp_fd = setup_listen_socket((sockaddr_t *) aip->ai_addr);
if(tcp_fd < 0)
continue;
int udp_fd = setup_vpn_in_socket((sockaddr_t *) aip->ai_addr);
if(tcp_fd < 0) {
close(tcp_fd);
continue;
}
io_add(&listen_socket[listen_sockets].tcp, handle_new_meta_connection, &listen_socket[listen_sockets], tcp_fd, IO_READ);
io_add(&listen_socket[listen_sockets].udp, handle_incoming_vpn_data, &listen_socket[listen_sockets], udp_fd, IO_READ);
if(debug_level >= DEBUG_CONNECTIONS) {
char *hostname = sockaddr2hostname((sockaddr_t *) aip->ai_addr);
logger(DEBUG_CONNECTIONS, LOG_NOTICE, "Listening on %s", hostname);
free(hostname);
}
listen_socket[listen_sockets].bindto = bindto;
memcpy(&listen_socket[listen_sockets].sa, aip->ai_addr, aip->ai_addrlen);
listen_sockets++;
}
freeaddrinfo(ai);
return true;
}
/*
Configure node_t myself and set up the local sockets (listen only)
*/
@ -883,73 +962,25 @@ static bool setup_myself(void) {
}
} else {
listen_sockets = 0;
config_t *cfg = lookup_config(config_tree, "BindToAddress");
int cfgs = 0;
do {
for(config_t *cfg = lookup_config(config_tree, "BindToAddress"); cfg; cfg = lookup_config_next(config_tree, cfg)) {
cfgs++;
get_config_string(cfg, &address);
if(cfg)
cfg = lookup_config_next(config_tree, cfg);
char *port = myport;
if(address) {
char *space = strchr(address, ' ');
if(space) {
*space++ = 0;
port = space;
}
if(!strcmp(address, "*"))
*address = 0;
}
struct addrinfo *ai, hint = {0};
hint.ai_family = addressfamily;
hint.ai_socktype = SOCK_STREAM;
hint.ai_protocol = IPPROTO_TCP;
hint.ai_flags = AI_PASSIVE;
int err = getaddrinfo(address && *address ? address : NULL, port, &hint, &ai);
free(address);
if(err || !ai) {
logger(DEBUG_ALWAYS, LOG_ERR, "System call `%s' failed: %s", "getaddrinfo", err == EAI_SYSTEM ? strerror(err) : gai_strerror(err));
if(!add_listen_address(address, true))
return false;
}
}
for(struct addrinfo *aip = ai; aip; aip = aip->ai_next) {
if(listen_sockets >= MAXSOCKETS) {
logger(DEBUG_ALWAYS, LOG_ERR, "Too many listening sockets");
return false;
}
for(config_t *cfg = lookup_config(config_tree, "ListenAddress"); cfg; cfg = lookup_config_next(config_tree, cfg)) {
cfgs++;
get_config_string(cfg, &address);
if(!add_listen_address(address, false))
return false;
}
int tcp_fd = setup_listen_socket((sockaddr_t *) aip->ai_addr);
if(tcp_fd < 0)
continue;
int udp_fd = setup_vpn_in_socket((sockaddr_t *) aip->ai_addr);
if(tcp_fd < 0) {
close(tcp_fd);
continue;
}
io_add(&listen_socket[listen_sockets].tcp, handle_new_meta_connection, &listen_socket[listen_sockets], tcp_fd, IO_READ);
io_add(&listen_socket[listen_sockets].udp, handle_incoming_vpn_data, &listen_socket[listen_sockets], udp_fd, IO_READ);
if(debug_level >= DEBUG_CONNECTIONS) {
hostname = sockaddr2hostname((sockaddr_t *) aip->ai_addr);
logger(DEBUG_CONNECTIONS, LOG_NOTICE, "Listening on %s", hostname);
free(hostname);
}
memcpy(&listen_socket[listen_sockets].sa, aip->ai_addr, aip->ai_addrlen);
listen_sockets++;
}
freeaddrinfo(ai);
} while(cfg);
if(!cfgs)
if(!add_listen_address(address, NULL))
return false;
}
if(!listen_sockets) {
@ -1045,7 +1076,8 @@ void close_network_connections(void) {
terminate_connection(c, false);
}
list_delete_list(outgoing_list);
if(outgoing_list)
list_delete_list(outgoing_list);
if(myself && myself->connection) {
subnet_update(myself, NULL, false);

View file

@ -116,7 +116,7 @@ static bool bind_to_interface(int sd) {
static bool bind_to_address(connection_t *c) {
int s = -1;
for(int i = 0; i < listen_sockets; i++) {
for(int i = 0; i < listen_sockets && listen_socket[i].bindto; i++) {
if(listen_socket[i].sa.sa.sa_family != c->address.sa.sa_family)
continue;
if(s >= 0)

View file

@ -1292,6 +1292,7 @@ const var_t variables[] = {
{"IffOneQueue", VAR_SERVER},
{"Interface", VAR_SERVER},
{"KeyExpire", VAR_SERVER},
{"ListenAddress", VAR_SERVER | VAR_MULTIPLE},
{"LocalDiscovery", VAR_SERVER},
{"MACExpire", VAR_SERVER},
{"MaxConnectionBurst", VAR_SERVER},