Add the ListenAddress option.
authorGuus Sliepen <guus@tinc-vpn.org>
Mon, 20 Jan 2014 20:19:13 +0000 (21:19 +0100)
committerGuus Sliepen <guus@tinc-vpn.org>
Mon, 20 Jan 2014 20:19:13 +0000 (21:19 +0100)
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
doc/tinc.conf.5.in
doc/tinc.texi
src/net.h
src/net_setup.c
src/net_socket.c
src/tincctl.c

index 3d5814d..536503b 100644 (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
index 54517f6..632c849 100644 (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.
index 42f17c2..6c09a01 100644 (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.
index 9a97276..c32c1c1 100644 (file)
--- 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"
index 858992c..daa296b 100644 (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;
-                               }
-
-                               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);
index 6828da7..30b36a7 100644 (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)
index 83efc93..c6d7582 100644 (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},