Commit cff5a84 removed the feature of binding outgoing TCP sockets to a local
address. We now call bind() again, but only if there is exactly one listening
socket with the same address family as the destination address of the outgoing
socket.
The order in which tinc initialized things was not completely correct. Now, it
is done as follows:
- Load and parse configuration files.
- Create all TCP and UDP listening sockets.
- Create PID file and UNIX socket.
- Run the tinc-up script.
- Drop privileges.
- Start outgoing connections.
- Run the main loop.
The PID file can only be created correctly if the listening sockets have been
set up ,as it includes the address and port of the first listening socket. The
tinc-up script has to be run after the PID file and UNIX socket have been
created so it can change their permissions if necessary. Outgoing connections
should only be started right before the main loop, because this is not really
part of the initialization.
The PID file was created before tinc-up was called, but the UNIX socket was
created afterwards, which meant one could not change the UNIX socket's owner or
permissions from the tinc-up script.
According to Debian, texi2html is deprecated and makeinfo --html should be used
instead. Automake already provides a html target that invokes makeinfo.
Automake finds the files in the subdirectories of src/ now that they are
properly declared in the _SOURCES variables. Using EXTRA_DIST would now cause
.o files to be included in the tarball.
When reloading the configuration file via the tinc command, the user will get
an error message if reloading has failed. However, no such warning exists when
sending a HUP signal. Previously, tincd would exit in both cases, but with a
zero exit code. Now it will exit with code 1 when reloading fails after a
SIGHUP, but tincd will keep running if it is signaled via the tinc command.
Instead, the tinc command will exit with a non-zero exit code.
The retry() function would only abort connections that were in progress of
being made, it wouldn't reschedule the outgoing connections that had been
sleeping.
As mentioned by Erik Tews, calling fchmod() after fopen() leaves a small window
for exploits. As long as tinc is single-threaded, we can use umask() instead to
reduce file permissions. This also works when creating the AF_UNIX control socket.
The umask of the user running tinc(d) is used for most files, except for the
private keys, invitation files, PID file and control socket.
In case no explicit netname of configuration directory is specified when
accepting an invitation, the netname specified in the invitation data is
used. However, this new netname is only known after making the connection
to the server. If the new netname conflicts with an existing one at the
client, we ask the user for a netname that doesn't conflict. However, we
should first finish accepting the invitation, so we don't run into the
problem that the server times out and cancels the invitation. So, we create
a random netname and store the files there, and only after we finish
accepting the invitation we ask the user for a better netname, and then
just rename the temporary directory to the final name.
If port 655 cannot be bound to when using the init command, tinc will try to
find a random port number that can be bound to, and will add the appropriate
Port variable to its host config file. A warning will be printed as well.
During the init command, tinc changed the umask to 077 when writing the public
and private key files, to prevent the temporary copies from being world
readable. However, subsequently created files would therefore also be
unreadable for others. Now we don't change the umask anymore, therefore
allowing the user to choose whether the files are world readable or not by
setting the umask as desired. The private key files are still made unreadable
for others of course. Temporary files now inherit the permissions of the
original, and the tinc-up script's permissions now also honour the umask.
This patch adds timestamp information to type 2 MTU probe replies. This
timestamp can then be used by the recipient to estimate bandwidth more
accurately, as jitter in the RX direction won't affect the results.
When replying to a PMTU probe, tinc sends a packet with the same length
as the PMTU probe itself, which is usually large (~1450 bytes). This is
not necessary: the other node wants to know the size of the PMTU probes
that have been received, but encoding this information as the actual
reply length is probably the most inefficient way to do it. It doubles
the bandwidth usage of the PMTU discovery process, and makes it less
reliable since large packets are more likely to be dropped.
This patch introduces a new PMTU probe reply type, encoded as type "2"
in the first byte of the packet, that indicates that the length of the
PMTU probe that is being replied to is encoded in the next two bytes of
the packet. Thus reply packets are only 3 bytes long.
(This also protects against very broken networks that drop very small
packets - yes, I've seen it happen on a subnet of a national ISP - in
such a case the PMTU probe replies will be dropped, and tinc won't
enable UDP communication, which is a good thing.)
Because legacy nodes won't understand type 2 probe replies, the minor
protocol number is bumped to 3.
Note that this also improves bandwidth estimation, as it is able to
measure bandwidth in both directions independently (the node receiving
the replies is measuring in the TX direction) and the use of smaller
reply packets might decrease the influence of jitter.
The hashing function that tinc uses is currently broken as it only looks
at the first 4 bytes of data.
This leads to interesting bugs, like the node UDP address cache being
subtly broken because two addresses with the same protocol and port (but
not the same IP address) will override each other. This is because
the first four bytes of sockaddr_in contains the IP protocol and port,
while the IP address itself is contained in the four remaining bytes
that are never used when the hash is computed.
Windows doesn't actually support it, but MinGW provides it. However, with some versions of
MinGW it doesn't work correctly. Instead, we vsnprintf() to a local buffer and xstrdup() the
results.
I believe I have found a bug in tinc on Linux when it is used with
Mode = router and DeviceType = tap. This combination is useful because
it allows global broadcast packets to be used in router mode. However,
when tinc receives a packet in this situation, it needs to make sure its
destination MAC address matches the address of the TAP adapter, which is
typically not the case since the sending node doesn't know the MAC
address of the recipient. Unfortunately, this is not the case on Linux,
which breaks connectivity.
Tinc now strictly limits incoming connections from the same host to 1 per
second. For incoming connections from multiple hosts short bursts of incoming
connections are allowed (by default 100), but on average also only 1 connection
per second is allowed.
When an incoming connection exceeds the limit, tinc will keep the connection in
a tarpit; the connection will be kept open but it is ignored completely. Only
one connection is in a tarpit at a time to limit the number of useless open
connections.
When LocalDiscovery is enabled, tinc normally sends broadcast packets during
PMTU discovery to the broadcast address (255.255.255.255 or ff02::1). This
option lets tinc use a different address.
At the moment only one LocalDiscoveryAddress can be specified.
Some options can take an optional argument. However, in this case GNU getopt
requires that the optional argument is right next to the option without
whitespace inbetween. If there is whitespace, getopt will treat it as a
non-option argument, but tincd ignored those without a warning. Now tincd will
allow optional arguments with whitespace inbetween, and will give an error when
it encounters any other non-option arguments.
The tinc binary now requires that all options for itself are given before the
command.
Using the tinc command, an administrator of an existing VPN can generate
invitations for new nodes. The invitation is a small URL that can easily
be copy&pasted into email or live chat. Another person can have tinc
automatically setup the necessary configuration files and exchange keys
with the server, by only using the invitation URL.
The invitation protocol uses temporary ECDSA keys. The invitation URL
consists of the hostname and port of the server, a hash of the server's
temporary ECDSA key and a cookie. When the client wants to accept an
invitation, it also creates a temporary ECDSA key, connects to the server
and says it wants to accept an invitation. Both sides exchange their
temporary keys. The client verifies that the server's key matches the hash
in the invitation URL. After setting up an SPTPS connection using the
temporary keys, the client gives the cookie to the server. If the cookie
is valid, the server sends the client an invitation file containing the
client's new name and a copy of the server's host config file. If everything
is ok, the client will generate a long-term ECDSA key and send it to the
server, which will add it to a new host config file for the client.
The invitation protocol currently allows multiple host config files to be
send from the server to the client. However, the client filters out
most configuration variables for its own host configuration file. In
particular, it only accepts Name, Mode, Broadcast, ConnectTo, Subnet and
AutoConnect. Also, at the moment no tinc-up script is generated.
When an invitation has succesfully been accepted, the client needs to start
the tinc daemon manually.
Most important is the annotation of xasprintf() with the format attribute,
which allows the compiler to give warnings about the format string and
arguments.