Commit Graph
106 Commits
Author SHA1 Message Date
Matthias Radestock 2b61bc91df cosmetic: remove blank log lines 2015-07-11 16:43:33 +01:00
Bryan Boreham cbcd5da680 Fix log message on peer move 2015-07-09 12:33:39 +01:00
David Wragg 501f40d9c1 Consolidate remote peer checks 2015-07-08 17:21:32 +01:00
David Wragg a6a74001bd Make TCPReceiver more closely mirror TCPSender 2015-07-08 08:53:53 +01:00
Bryan Boreham 013380c7e7 Update router code to use common logger 2015-07-01 12:26:23 +01:00
Matthias Radestock ab943d938a refactor: don't return encoder from handshake()
We did this for symmetry/completeness, but the encoder is actually
"consumed" by handshake() in the creation of the tcp senders, hence it
is in fact dangerous to return it.
2015-06-29 12:38:21 +01:00
Matthias Radestock 039a2030db refactor: make DF flag passing logic more obvious
...and provide a better abstraction for sending special frames.
2015-06-26 11:11:17 +01:00
David Wragg a311623bb9 Eliminate the Start method on LocalConnection 2015-06-23 16:08:43 +01:00
Matthias Radestock 3124ffede5 more forward/backward compatible protocol versioning
We now first exchange a very minimal header, consisting of just the
name of the protocol ("weave") and the min and max version (encoded as
a byte each) we support. This leaves all other details of the
protocol, including the contents of of the handshake, open.

Peers agree to communicate using the maximum mutually supported
version. We record this in the LocalConnection, though atm nothing is
making use of that information.

We set a shorter than normal deadline (10s instead of 60s) for the
header exchange, so we detect version mismatches, garbage, or
non-responsive remotes quickly.

We reset the protocol version to '1' since this is a fresh start; old
weaves will not be able to parse the header.

Closes #871.
2015-06-16 11:00:41 +01:00
Matthias Radestock fbf2208e80 simplify fragmentation test
This is how fragmentation tests used to happen:

    A: conn.stackFrag = false
    A->B: StartFragmentationTest
    B->A: send large non-DF UDP packet
    A->B: FragmentationReceived
    B: conn.stackFrag = true

conn.stackFrag = true means "UDP packets sent to the other peer get
correctly fragmented and re-assembled".

Note how A clears its own stackFrag flag, but the test establishes
whether fragmentation works for B->A, and ends up setting stackFrag on
B. Not only is this weird, it also means that if A previously had
stackFrag=true, it will now start fragmenting packets until B
initiates a fragmentation test.

The new sequence is:

    A: conn.stackFrag = false
    A->B: send large non-DF UDP packet
    B->A: FragmentationReceived
    A: conn.stackFrag = true

Closes #746.
2015-06-10 07:41:37 +01:00
Matthias Radestock f36af211c0 cosmetic 2015-05-22 15:41:29 +01:00
David Wragg dfa93436c2 Fix use of "break" within "select" in LocalConnection actorLoop
From the golang spec: 'A "break" statement terminates execution of the
innermost "for", "switch", or "select" statement within the same
function.'  But the LocalConnection actorLoop seems to assume it
skips the "select" and terminates the enclosing "for".

As a result, if something did conn.Shutdown(nil), it would not actually
shutdown the connection. It turns out this is never a problem, because
Shutdown is always called with a non-nil error, even though there is code
in a few places to handle the nil case. By cleaning that up, the dubious
"break" is eliminated.
2015-05-22 11:26:30 +01:00
Matthias Radestock 505ced5cc8 move localRefCount accounting responsibility from Peer to Peers
This makes for a more symmetric API - Peers.FetchWithDefault does the
increment and Peers.Dereference does the decrement, whereas previously
the latter was done by Peer.DecrementLocalRefCount.

This does increase lock granularity, i.e. modifying any ref count now
requires a write lock on Peers.
2015-05-11 17:19:23 +01:00
Matthias Radestock 0cbbe9bac3 make Peer.String() return the common case
When we want a string representation of a Peer, we nearly always just
want the full name. So make String() return that and introduce Info()
to return expanded info.
2015-05-10 23:58:23 +01:00
Matthias Radestock 137776bcb2 remove decryptor shutdown
we only needed it because of the nonce channels
2015-04-30 14:47:16 +01:00
Matthias Radestock e223899982 remove nonce transmission from protocol 2015-04-30 14:47:16 +01:00
Matthias Radestock 696bfe464c replace random nonces with message sequence numbers in tcp crypto
Since TCP is reliable, we do not need to transmit the nonce itself;
the recipient can simply construct the nonce from its own message
counter. Hence there is no message envelope and we can thus remove a
whole layer of encoding.
2015-04-30 14:47:16 +01:00
Matthias Radestock ea4e84cdd3 refactor: (almost) remove dependency of crypto on LocalConnection 2015-04-27 07:14:30 +01:00
David Wragg dceef04ffa Separate Forwarder and ForwarderDF
Much of the state and code associated with Forwarder only applied in the
DF case.
2015-04-18 10:38:05 +01:00
Matthias Radestock 57b7276ceb create forwarders before AddConnection, if we can
This is beneficial for reasons explained in the comments. And we used
to do that previously, when initHeartbeats was called before
AddConnection.
2015-04-15 20:55:42 +01:00
Matthias Radestock 9b25815641 don't send heartbeats on new but ultimately invalid connections
It is wasteful and can trip up crypto at the other end. See comment
for why.

To accomplish this we move the responsibility for shutting down the
new, invalid connection from LocalPeer.AddConnection to its caller,
i.e. LocalConnection.run. So rather than always firing up all the
connection machinery and getting told asynchronously by AddConnection
to shut it all down again, we now only fire it up if AddConnection
succeeded.
2015-04-15 20:04:40 +01:00
Matthias Radestock b1fd1334b4 always include the remote address in connection log entries
in addition to the peer name

This is handy for debugging, especially in situations when there are
multiple connections in flight to the same peer.
2015-04-14 22:14:29 +01:00
Bryan Boreham 8ad244e0f9 Let ConnectionMaker report the reason the connection broke 2015-04-13 14:35:27 +01:00
Bryan Boreham f8ec56ea79 Move connection shutdown logging into shutdown() 2015-04-13 14:21:11 +01:00
Matthias Radestock 40d127d9a4 make connection heartbeating independent of periodic gossip
This was a convenient but rather suspect coupling. In particular, it
will break if the period gossip logic changes s.t. it no longer sends
gossip on all connections every interval.
2015-04-09 11:44:13 +01:00
Matthias Radestock e639007b62 eliminate AddConnection/RemoveConnection race
We were invoking AddConnection in a separate goroutine, and this could
result in RemoveConnection getting invoked first, resulting in bogus
entries in our connection map, which would effectively prevent
connectivity to the other peer until the stale entry got removed
through a lost tie break. And if, per chance, the remote end ran into
exactly the same race condition, then connectivity between that peer
and ourself was broken permanently.

Hitting this race condition required a combination of a) no UDP
heartbeat being received for HeartbeatTimeout(30s) - this causes the
actorLoop to terminate and RemoveConnection to be invoked, and b) the
AddConnection invocation getting delayed for at least that long.
2015-04-09 06:34:35 +01:00
Matthias Radestock fdea7d93f9 refactor: improve abstraction in connection gossip handling
- turn previously free-floating functions into methods on GossipChannel
- move gossip message type dispatch from connection into router
2015-04-03 19:38:01 +01:00
Matthias Radestock d708cb7d4e prevent PMTUVerfied handling from blocking
...due to blocking on sending the pmtu to the forwarder, because the
forwarder has terminated.

Fixes #464.
2015-03-29 14:04:28 +01:00
Matthias Radestock fc22f9664e refactor: improve encapsulation of Forwarder 2015-03-29 12:14:32 +01:00
Matthias Radestock b6448fa196 more idiomatic closing of forwarders, by closing a 'finished' channel
The previous scheme set the reference to main channel to nil and then
drained it. That was racy and hence could lead to deadlock in the
following scenario:

1. multiple senders obtain the main channel from the stored reference
2. main channel reference is set to nil
3. main channel is drained
4. multiple senders send to main channel

So the issue here is that the entire forwarder stopping sequence
occurs between senders obtaining of the reference to the main channel,
and then sending on that channel. If more senders sent than there is
capacity in the channel then this leads to deadlock.
2015-03-28 20:58:42 +00:00
Matthias Radestock 4dbb3d97e0 cosmetic: remove dead code 2015-03-28 20:33:06 +00:00
Matthias Radestock 33520ea230 include peer nickname in display output whenever possible 2015-03-27 05:25:28 +00:00
Matthias Radestock b83f4c9ee9 gofmt 2015-03-26 13:31:11 +00:00
Matthias Radestock 31efd76797 eliminate potential deadlock/dealy in connection.Shutdown
Treat the no-error case the same as the error case...
- use the standard sendAction to communicate with the actor, which
  ensures that we don't block when the actor has terminated
- run in a goroutine to prevent delaying the caller when the actor's
  channel is backed up.
2015-03-26 13:25:23 +00:00
Matthias Radestock 48c73598c4 convert the connection actor to use closures
The only slight trickiness is Shutdown. Generally connections shut
down when an error is encountered, but we also need to be able to shut
down connections w/o an error. Rather than making all the actor
methods return a (terminated bool, error) tuple, we signal the
non-error shutdown by sending nil as the action (instead of a
closure).
2015-03-25 05:14:17 +00:00
Bryan Boreham d87e13b999 Re-order Connection fields more consistently 2015-03-11 14:01:07 +00:00
Bryan Boreham 89fce7f98a Add an 'outbound' field on each connection,
and only attempt new connections to the remote side of outbound connections.
2015-03-11 11:56:47 +00:00
Matthias Radestock 6f89ead9bd log all connection lifecycle events on local peer
There were situations, such as deleting another connection as result
of it losing a tie-break, where lifecycle events of connections on the
local peer weren't logged.

Fixing that entails moving the logging closer to (i.e. right after)
the state change action. This in turn requires promotion of logging to
the Connection interface instead of it LocalConnection. Which in turn
requires a change from 'log' to 'Log', for consistency with other
interface methods.

Fixes #449.
2015-03-10 14:42:18 +00:00
Bryan Boreham 6437caddf2 Reworded comment 2015-02-27 15:02:30 +00:00
Bryan Boreham 139758256b Make clear that tcp-send methods are not handlers and may be called outside of queryLoop 2015-02-27 14:52:47 +00:00
Bryan Boreham 657125fd54 Send to tcpSender on the calling thread, so the Actor channel doesn't get clogged up 2015-02-27 14:12:32 +00:00
Adam Harrison d031e8d246 Merge branch 'master' into 373_detect_udp_connectivity_breakage
Conflicts:
	router/connection.go
2015-02-24 18:53:39 +00:00
Adam Harrison 7618ae033c Simplify to single heartbeat timer 2015-02-24 18:31:18 +00:00
Bryan Boreham 78d2a3e466 Add 'finished' chan to stop callers blocking on closed connection 2015-02-24 10:48:52 +00:00
Adam Harrison 0a0ed7a871 Tear down connections on prolonged loss of UDP heartbeat 2015-02-23 15:29:11 +00:00
Matthias Radestock e2818f883c refactor: move and simplify handshake logic 2015-02-04 16:49:57 +00:00
Matthias Radestock f25696c245 prevent premature gc of peers from topology
Topology gossip can be received on a connection before that connection
has been marked as 'established'. If we do not include unestablished
connections in topology gossip, then parts of the topology can be
removed by gc, since they are deemed unreachable, which can cause
subsequent incremental topology gossips to be rejected due to them
containing references to peers that have been removed by the gc.

Including unestablished connections in topology gossip requires:

- removing the check for 'established' connections in the encoding of
  peers for topology gossip,
- adding the 'established' flag to the internal representation of
  remote connections,
- adding the 'established' flag to to encoding of connections in
  topology gossip,
- treating the *addition* of a connection as an update to the local
  peer.

The latter entails

- bumping the peer version on connection addition (as well as on
  marking 'established' as previously),
- bumping the peer version *unconditionally* on connection deletion
  (rather than only when the connection was 'established', as
  previously),
- moving the initial sending of complete topology on the connection,
  and the broadcasting of a topology update containing the two peers
  at the ends of the connection, from the connection 'established'
  logic to the connection addition logic,
- broadcasting just an topology update for the local peer when a
  connection gets marked as 'established',
- broadcasting a topology update for the local peer *unconditionally*
  when a connection is removed (rather than only doing this when the
  connection was 'established', as previously).
2015-02-01 16:52:39 +00:00
Matthias Radestock b43e5debb1 gofmt 2015-02-01 16:50:43 +00:00
Matthias Radestock 2d135f3da0 prevent premature peer gc
Caused by a race between adding the connection to our map and
receiving a topology gossip on it.

We remove the race by making AddConnection synchronous and invoking it
prior to entering the tcp receive loop.

Fixes #367.
2015-01-31 22:29:07 +00:00
Matthias Radestock c1dd2e3a09 tear down connections that have no (initial) udp connectivity
Prior to the introduction of gossip, a tcp connection would not see
regular communication until it had been marked as 'established',
because the topology communication scheme would periodically send
'FetchAll' only after that. So in the absence of udp connectivity we'd
time out. The timeout could of course occur for other reasons, namely
disrupted tcp connectivity, but the combination of a timeout and 'not
established' pointed to a lack of udp connectivity as a likely
cause. So that's what we checked for.

In the new gossip-based topology communication, however, gossip (of
anything, not just topology) occurs as soon as we have tcp
connectivity. Therefore we will no longer encounter a timeout when
just udp connectivity is disrupted. Which makes our check invalid.

So instead we introduce a one-shot timer, created on handshake
completion and cancelled when we mark the connection as
'established'. When the timer fires, and the connection still hasn't
been established, we shut down the connection with an error indicating
lack of udp connectivity.
2015-01-29 12:41:52 +00:00