From 38adc8bf548c2c465d5f4147866c3d3f9112d3a8 Mon Sep 17 00:00:00 2001 From: Guus Sliepen Date: Mon, 20 Jan 2014 21:19:13 +0100 Subject: [PATCH] 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. --- bash_completion.d/tinc | 2 +- doc/tinc.conf.5.in | 48 ++++++++----- doc/tinc.texi | 24 ++++--- src/net.h | 1 + src/net_setup.c | 158 +++++++++++++++++++++++++---------------- src/net_socket.c | 2 +- src/tincctl.c | 1 + 7 files changed, 144 insertions(+), 92 deletions(-) diff --git a/bash_completion.d/tinc b/bash_completion.d/tinc index 3d5814d4..536503bb 100644 --- a/bash_completion.d/tinc +++ b/bash_completion.d/tinc @@ -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 diff --git a/doc/tinc.conf.5.in b/doc/tinc.conf.5.in index 54517f62..632c8495 100644 --- a/doc/tinc.conf.5.in +++ b/doc/tinc.conf.5.in @@ -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. diff --git a/doc/tinc.texi b/doc/tinc.texi index 42f17c20..6c09a01d 100644 --- a/doc/tinc.texi +++ b/doc/tinc.texi @@ -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 = (no) When enabled, tinc will try to detect peers that are on the same local network. diff --git a/src/net.h b/src/net.h index 9a97276c..c32c1c14 100644 --- a/src/net.h +++ b/src/net.h @@ -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" diff --git a/src/net_setup.c b/src/net_setup.c index 858992c8..daa296bd 100644 --- a/src/net_setup.c +++ b/src/net_setup.c @@ -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; - } - - 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++; - } + 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; + } - 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); diff --git a/src/net_socket.c b/src/net_socket.c index 6828da7a..30b36a74 100644 --- a/src/net_socket.c +++ b/src/net_socket.c @@ -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) diff --git a/src/tincctl.c b/src/tincctl.c index 83efc935..c6d75824 100644 --- a/src/tincctl.c +++ b/src/tincctl.c @@ -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}, -- 2.39.5