An error in SendProtocolMsg indicates that the connection is
permanently broken. So there is no point continuing to attempt to send
gossip on it.
The connection will eventually be removed from LocalPeer, and any
associated GossipSenders will be gc'ed, but that doesn't happen
straight away, so this change is about not doing unnecessary work in
the meantime.
This is a one-sided operation, so we shouldn't be touching the
remote's Peers.
Also fix the associated test... When both 1 and 2 have lost their
connection to 3, they no longer have a route to 3 and hence gc it.
And because they no longer have a route, gossip from them won't get
through to 3.
We were creating peers with NewPeerFrom, which copies the Version (but
not connections). That's not what happens for a real connection, where
remote peers are always created with an initial version of 0.
The mistake results in brokenness: when we subsequently receive a
topology update for that peer with the same version then we ignore it,
even though the update contains connection information which is new to
us.
Related, it is conceptually wrong to call FetchWithDefault on the
remote router. That is not what happens for a real connection - the
FetchWithDefault occurs in the symmetric counterpart.
If overlay encryption is required between peers, the fast datapath
forwarder returns an error rather than aborting the process, allowing
the overlay switch to fall back gracefully to an encrypted sleeve
overlay connection.
Add a 'Trusted' feature to the protocol exchange which indicates whether
the remote considers us to be reachable via a secure network; overlay
encryption will be disabled in the event that both peers agree. For
backwards compatibility purposes a default value of untrusted is assumed
if the feature is missing.
Instead of calculating all routes in one go, we calculate them when
needed.
Except for unicast routes, and broadcast routes from ourself. We still
calculate those eagerly because they are needed to route locally
captured/fdp-missed packets, and performing route calculcations in
that critical path increases the chances of dropping packets due to
not keeping up.
Well, technically only the established & symmetric routes are needed
for the data plane, but we might as well calculate the equivalent
routes for the control plane - it keeps the code simple and uniform.
Calculating all routes is O(n_peers^2), so this change represents a
huge saving in situations where not all of them are needed, which is
quite common, especially when the topology is evolving rapidly.
One downside of this new approach is that we can no longer suppress
OnChange callbacks when the data plane routes remain unchanged. This
means we will be purging the FDP flows more frequently than before,
e.g. when only the control plane topology changed, e.g. new
connections appeared that haven't been marked as 'established'
yet. It's a small price to pay, fixable if we really care, and in any
case making FDP flow invalidation more fine-grained would be a better
approach.
Fixes#1761.
real Connections do not process gossip until after AddConnection.
mockChannelConnection wasn't behaving in that way, which resulted in
some sporadic test failures since it would cause some gossip to get
processed too early, which in turn would trigger gc of some of the
just-received peers, which we would then find missing in the test
assertions.
To fix this we introduce a latch on mockChannelConnection which closes
when the remote end is ready to receive gossip. Effectively this
emulates the entering of LocalConnection.receiveTCP after the call to
AddConnection completes.
Unfortunately an open latch will cause sendPendingGossip to block. We
get around that by abandoning checks in "half-connected" connection
states.
Fixes#1765.
This is more efficient since broadcasting about other peers is
strictly redundant. Any change to the peers will be broadcast by
those peers anyway. And SendAllGossipDown takes care of informing new
peers about others on first connect, and periodic gossip take care of
missed broadcasts.
The only reason we did broadcast about the remote peer is to reduce
the liklihood of UnknownPeerError and the associated throwing away of
gossip. This was always a bit of kludge, and is no longer necessary
since UnknownPeerError is gone.
Now peers only ever broadcast information about themselves. Which,
unlike information about other peers, is always authorative and most
up-to-date. Which is nice.
when encountering an unknown peer in received gossip, rather than
ignoring the entire gossip we create a placeholder Peer. This has
version 0 and uid 0, which means it will be replaced by any complete
peer information subsequently received via gossip.
This was missing.
There are two parts to this change:
1. updating the Peers.byName mapping when HasShortID changed (in
addition to the existing update on ShortID change). We need to be
careful about the sequencing here - both ShortID and HasShortID must
be set between the calls to deleteByShortID and addByShortID, since
the former is meant to delete the existing entry (hence use the old
values) and the latter add the new entry (hence use the new values).
2. update a peer when the received info has the same version and UID
but the current record has HasShortID=false, and the received info has
HasShortID=true. This caters for the case where we receive an update
about a weave 1.2+ peer first via a pre-1.2 peer (which does not
transmit ShortIDs, and always sets HasShortID=false) and subsequently
via a 1.2+ peer. The former update will force us to communicate with
that peer over the 'sleeve' overlay. We want to accept the latter
update, even though the version is the same, in order to update the
ShortID and HasShortID, so we can switch to fastdp.
Fixes#1731.
We need Connection.Shutdown to be non-blocked, and the way we achieved
this is by spawning a goroutine. However, there can be a potentially
unbounded number of Connection.Shutdown invocations. For example, if
the TCP connection has gone bad then any Connection.SendProtocolMsg
invocation will end up invoking Connection.Shutdown. There can be many
such invocations until the Connection has been removed from the
various data structures (e.g. LocalPeer.connections and
GossipSender.senders). Hence we can end up with with an unbounded
number of spawned goroutines.
To fix this, we introduce a one-element error channel on the
connection. Connection.Shutdown does a non-blocking send on that, and
the Connection.actorLoop reads from it and terminates.
This will throw away all but the first error encountered, but that is
fine and not much different from what was happening before (where we'd
throw away all but one of the errors encountered).