Separate login and message replay timestamps (proper fix for #1551, supersedes #1889)#2834
Draft
usrflo wants to merge 1 commit into
Draft
Separate login and message replay timestamps (proper fix for #1551, supersedes #1889)#2834usrflo wants to merge 1 commit into
usrflo wants to merge 1 commit into
Conversation
…o prevent being stuck using different timer values
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Properly fixes the silent-message-rejection issue (#1551) without introducing the login regression that #1889 caused (after the first admin login, no subsequent login was possible). It does so by tracking login replay and message replay in two separate per-client fields instead of sharing one.
Background
#1889 addressed #1551 by no longer storing the login timestamp into
client->last_timestamp. The reasoning: login packets carry the companion device's RTC (BaseChatMesh::sendLogin→getRTCClock()->getCurrentTimeUnique()), while regular messages carry the app clock (passed as atimestampparameter tosendMessage/sendCommandData). If the RTC runs ahead of the app clock, writing the RTC value intolast_timestampon login caused later, lower app-clock messages to be dropped by the replay check (sender_timestamp < last_timestamp).However, #1889 left the login replay check comparing against
last_timestampand left the message handlers updatinglast_timestamp. This inverts the failure:last_timestampis still low/zero).onPeerDataRecvTXT/REQ handlers) driveslast_timestampup to a large app-clock value.sender_timestamp <= client->last_timestamptrips →possible replay attack→ the login is silently dropped (client times out).Reverting #1889 (the stock firmware) makes login work again, but reintroduces #1551. So each direction is broken depending on which clock leads.
Root cause
A single replay variable,
client->last_timestamp, is fed and checked by two independent clocks:getCurrentTimeUnique()) — also used only as a uniqueness blob for the packet hash.Neither clock is monotonic relative to the other, so any scheme that mixes them fails in one direction or the other. Note that
hasSeen()(the mesh-layer packet-hash dedup) does not substitute here: it only catches exact-duplicate packets, not the "is this login newer than the last one" ordering the timestamp check provides.Fix
Split the two replay tracks:
ClientInfo::last_login_timestamp.last_login_timestamp.last_timestamp.Result:
getCurrentTimeUnique()is monotonic per device, every login's timestamp strictly exceeds the previous one → repeated logins work again (regression fixed).last_timestamp, which is no longer poisoned by the login's RTC value → messages are no longer rejected when the RTC runs ahead (Fix Room Server message delivery failure when companion RTC differs from app clock #1551 fixed).clock sync/timerefuse to go backwards) no longer matters, because the two replay tracks are independent.Applied uniformly to the three servers that share this pattern:
simple_room_server,simple_repeater,simple_sensor.Backward compatibility
No wire-protocol change. Old clients keep working against a patched server (the server just tracks login and messages separately), and patched clients work against an unpatched server.