Skip to content

Commit bc2c8ac

Browse files
committed
clusterer_controller: README - JOIN_REQs and beacons sent again, replies to the node itself, T34
1 parent 6dd88c0 commit bc2c8ac

1 file changed

Lines changed: 4 additions & 1 deletion

File tree

‎modules/clusterer_controller/README.md‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ Each property is checked by the network-namespace suite (*test/netns_tests.sh*,
152152
| Several nodes joining at once, and a whole cluster starting cold at once, get one master and distinct node ids | T31 | - |
153153
| A JOIN_REJECT recorded and sent again does not stop a joining node; one it earns still does | T32 | - |
154154
| A node's packets recorded and sent again from another address meet that node's replay window: its old GOODBYE removes nothing | T33 | - |
155+
| A JOIN_REQ recorded and sent again from another address admits nobody again and draws no reply there | T34 | - |
155156

156157
Not tested yet: a full peer table; a static *clusterer* cluster beside controller clusters on one node.
157158

@@ -188,6 +189,8 @@ The session key is generated once, when the first node bootstraps the cluster, a
188189

189190
Every JOIN_REJECT carries the nonce of the packet it refuses, and the joining side acts on it only if that nonce is one of its own last JOIN_REQs. Bootstrap packets have no replay window, so this is what keeps a refusal recorded once - say, while the node had a wrong password - from being sent again by anyone on the group to stop the node at a later start. A refusal from an older master, which carries no nonce, is still acted on for different settings or a different MTU, which the node checks against what it runs, but not for the bare refusal.
190191

192+
The two bootstrap packets that change state where they land are guarded the same way: a JOIN_REQ or beacon seen lately - by its header nonce, random per packet - is dropped as a repeat, and one from a previous life of a node whose current life is still heard (an incarnation lower than the one held, from a node heard within two query periods) is dropped too; a node that really restarted with a lower incarnation, its clock stepped back, is let in once its old life has fallen silent. A master's replies to a JOIN_REQ go to the joiner at its own address, and a MASTER_PROBE or RESYNC is answered only for a member and only at its own address - never to a datagram's source, which anyone can set.
193+
191194
On the joining side, a received JOIN_REJECT is acted on only if it is addressed to this node *and* this node currently has an admission request outstanding (*join_pending*). A member that is not asking to be admitted ignores JOIN_REJECT unconditionally, so a node with the correct password can never be evicted by a peer.
192195

193196
The guard is deliberately the pending request and not the *CL_CTR_NODE_NEW* state. After a simultaneous cold start both nodes can reach the join deadline and self-promote, and the loser then merges into the winner by sending a fresh JOIN_REQ *from the active state*. Keying on the state label would have that node discard the refusal of a join it had just made. It acts on the refusal, but differently from a joining node: a node still joining shuts down, while an established node abandons the merge and stays with its own partition (see *Split-brain handling*).
@@ -1638,7 +1641,7 @@ In all five failover tests (1–5): exactly one node held the active sharing tag
16381641

16391642
### Automated tests
16401643

1641-
*test/netns_tests.sh* runs the module end to end on one host: four network namespaces on a private bridge, every node in two clusters that share one port. It covers cold start, consumer delivery, restart and node_id reuse, partition heal, refusal of a foreign or mismatched cluster, a pinned cluster identity, recorded traffic sent again (while the sender runs and after it restarts), a MEMBER_LIST that arrives after a newer one, the *tm* anycast Via cid before and after the node has an id, a member and the master paused past every timeout, a receive buffer overflowed by a flood, a member that alone stops hearing the master, maintenance (a backup and then the master taken out of election and tags, and back), cuts one-sided and both ways and the heals after them, a killed master (also one restarted at once), a lossy and jittery link, a stuttering member, a cut-off master releasing the active tag, another node put into full maintenance, a member on the previous build, the statistics and the events, datagrams that are not packets of the cluster (*test/malformed_test.py* sends them: without the password, and sealed with the bootstrap key it derives as the module does - every type byte, payloads at every field boundary - from addresses that are no member, a member's and the master's), several nodes, or all four, starting at once, a JOIN_REJECT recorded and sent again to a node that is joining, and a node's packets sent again from another address. It needs root, iproute2, iptables and python3:
1644+
*test/netns_tests.sh* runs the module end to end on one host: four network namespaces on a private bridge, every node in two clusters that share one port. It covers cold start, consumer delivery, restart and node_id reuse, partition heal, refusal of a foreign or mismatched cluster, a pinned cluster identity, recorded traffic sent again (while the sender runs and after it restarts), a MEMBER_LIST that arrives after a newer one, the *tm* anycast Via cid before and after the node has an id, a member and the master paused past every timeout, a receive buffer overflowed by a flood, a member that alone stops hearing the master, maintenance (a backup and then the master taken out of election and tags, and back), cuts one-sided and both ways and the heals after them, a killed master (also one restarted at once), a lossy and jittery link, a stuttering member, a cut-off master releasing the active tag, another node put into full maintenance, a member on the previous build, the statistics and the events, datagrams that are not packets of the cluster (*test/malformed_test.py* sends them: without the password, and sealed with the bootstrap key it derives as the module does - every type byte, payloads at every field boundary - from addresses that are no member, a member's and the master's), several nodes, or all four, starting at once, a JOIN_REJECT recorded and sent again to a node that is joining, a node's packets sent again from another address, and a JOIN_REQ sent again from another address. It needs root, iproute2, iptables and python3:
16421645

16431646
```bash title="netns tests"
16441647
NEW=/usr/local OLD=/opt/opensips-wire1 PREV=/opt/opensips-prev ./test/netns_tests.sh

0 commit comments

Comments
 (0)