macula_peer_versions (macula v13.2.1)

View Source

Which handshake version a client dials each node with, and the node-wide handshake counters (plans/DESIGN_NEIGHBOUR_CHANNEL_BINDING.md sections 3, 4 and 6).

A node never seen is dialled with version 5. A node that refused a v5 CONNECT with unsupported_version is dialled with version 4 for the next 10 minutes, so a slow roll does not pay a failed v5 handshake on every new connection, then with version 5 again. A node that completed one v5 handshake in this run is never dialled with version 4 again: a later unsupported_version from it is refused as a downgrade, which also refuses a station rolled back below v5 until forget_v5_peer/1 is called for it or this node restarts. There is no timer on that memory, because a timer is also an attacker's wait.

Fallbacks are counted per node. The first is expected while the fleet rolls; from the second, the caller logs a warning at most once a minute per node.

This process only owns the table. Every read and write goes to the table directly, so no connection queues on it.

Summary

Functions

A v5 handshake with NodeId completed: it is never dialled with version 4 again in this run, and every v4 connection this node dialled to it that is still open is told to close as a downgrade ({v5_completed_elsewhere, NodeId}). With v4_completed/2 registering before it reads, no interleaving keeps a v4 connection (macula#53).

Count one handshake event, node-wide.

Every handshake counter, zero included.

The version to put in CONNECT to NodeId at Now.

Whether to log a warning for NodeId's refused downgrades at Now: from the first, at most once a minute, with how many there have been.

Whether to log a warning for NodeId's fallbacks at Now: from its second fallback, at most once a minute.

Forget that NodeId completed a v5 handshake, so a station deliberately rolled back below v5 is dialled again. An operator action, never automatic.

Count a refused downgrade for NodeId, as the connection that refuses it does.

NodeId refused a v5 CONNECT with unsupported_version. A node seen on v5 in this run is refused as a downgrade; any other falls back to version 4 for 10 minutes, with its fallback count.

A v4 handshake with NodeId, dialled by Pid, completed. Pid is registered first, then the memory is read: when NodeId completed v5 meanwhile it is refused as a downgrade; otherwise it stays registered until v4_ended/2, so a v5 completion after it reaches it (completed_v5/1). Each table operation is atomic, so in every interleaving either this read sees the v5 completion or that completion sees this registration (macula#53). That rests on each table operation completing before the next one of the same process starts, on the table's own locking, rather than on any cross-key ordering ETS documents. Only a node's own dials register: a station's accepted connections are never reached.

The v4 connection Pid dialled to NodeId ended.

Types

counter/0

-type counter() ::
          v4_connections | v5_connections | v4_control_frames | v4_fallbacks | v5_downgrade_refused |
          v4_hello_to_v5_connect | session_proof_invalid | session_proof_missing | session_proof_rate |
          exporter_unavailable.

Functions

completed_v5(NodeId)

-spec completed_v5(<<_:256>>) -> ok.

A v5 handshake with NodeId completed: it is never dialled with version 4 again in this run, and every v4 connection this node dialled to it that is still open is told to close as a downgrade ({v5_completed_elsewhere, NodeId}). With v4_completed/2 registering before it reads, no interleaving keeps a v4 connection (macula#53).

count(Name)

-spec count(counter()) -> ok.

Count one handshake event, node-wide.

counters()

-spec counters() -> #{counter() => non_neg_integer()}.

Every handshake counter, zero included.

dial_version(NodeId, Now)

-spec dial_version(<<_:256>>, integer()) -> 4 | 5.

The version to put in CONNECT to NodeId at Now.

downgrade_warning(NodeId, Now)

-spec downgrade_warning(<<_:256>>, integer()) -> no_warning | {warn, pos_integer()}.

Whether to log a warning for NodeId's refused downgrades at Now: from the first, at most once a minute, with how many there have been.

fallback_warning(NodeId, Now)

-spec fallback_warning(<<_:256>>, integer()) -> no_warning | {warn, pos_integer()}.

Whether to log a warning for NodeId's fallbacks at Now: from its second fallback, at most once a minute.

forget_v5_peer(NodeId)

-spec forget_v5_peer(<<_:256>>) -> ok.

Forget that NodeId completed a v5 handshake, so a station deliberately rolled back below v5 is dialled again. An operator action, never automatic.

handle_call(Request, From, State)

handle_cast(Request, State)

init(_)

refuse_downgrade(NodeId)

-spec refuse_downgrade(<<_:256>>) -> downgrade_refused.

Count a refused downgrade for NodeId, as the connection that refuses it does.

seen_v5(NodeId)

-spec seen_v5(<<_:256>>) -> boolean().

start_link()

-spec start_link() -> {ok, pid()}.

unsupported_version(NodeId, Now)

-spec unsupported_version(<<_:256>>, integer()) -> downgrade_refused | {fall_back, pos_integer()}.

NodeId refused a v5 CONNECT with unsupported_version. A node seen on v5 in this run is refused as a downgrade; any other falls back to version 4 for 10 minutes, with its fallback count.

v4_completed(NodeId, Pid)

-spec v4_completed(<<_:256>>, pid()) -> ok | downgrade_refused.

A v4 handshake with NodeId, dialled by Pid, completed. Pid is registered first, then the memory is read: when NodeId completed v5 meanwhile it is refused as a downgrade; otherwise it stays registered until v4_ended/2, so a v5 completion after it reaches it (completed_v5/1). Each table operation is atomic, so in every interleaving either this read sees the v5 completion or that completion sees this registration (macula#53). That rests on each table operation completing before the next one of the same process starts, on the table's own locking, rather than on any cross-key ordering ETS documents. Only a node's own dials register: a station's accepted connections are never reached.

v4_ended(NodeId, Pid)

-spec v4_ended(<<_:256>>, pid()) -> ok.

The v4 connection Pid dialled to NodeId ended.