mirror of
https://github.com/alexhopeoconnor/firmware.git
synced 2026-10-16 00:21:35 +10:00
fix(mesh): coerce coordinate traffic to the position channel on event builds (#11545)
Under USERPREFS_BLOCK_POSITION_ON_EVENT_CHANNEL every coordinate packet a client aimed at the event channel was rejected with the "Location sharing is disabled on this channel" notification - including the phone's own location feed. Both apps hand a GPS-less node its fix as a POSITION_APP packet addressed to the node itself on channel 0; that packet never leaves the device (Router::sendLocal delivers it locally) but resolved to the event channel and was dropped before PositionModule saw it. Result: the toast on every location tick, and nodes without a GPS never learned a position to share on their private channel. Position traffic now converges on the position channel - findPositionChannel(), the first channel with non-zero on-wire precision, which is never the event channel: - From-us-to-us coordinate packets are exempt from the event block. - Local coordinate sends aimed at the event channel (phone share-location, request-position, waypoints, any module/UI originator) are moved onto the position channel in Router::sendLocal and PhoneAPI instead of rejected. The client notification is only sent when no channel carries positions at all. - A position request DM'd to us on the event channel is answered on the position channel at that channel's precision (request_id preserved, same reply throttle); the requester's coordinates are still not stored, forwarded, relayed or published. want_response from the bitfield is merged before the event-channel decode short-circuit so such requests are seen. - PositionModule::sendOurPosition, positionUnchangedSinceLastSend and MeshService::trySendPosition use the shared helper instead of three copies of the same walk. Non-event builds are unaffected: the coercion compiles out and the helper matches the previous walk. Tests: coverage-event-policy (test_event_channel_phone_api, test_event_channel_router, test_position_precision, test_mqtt, test_nexthop_routing) and the same suites with the policy off. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
8fe246e250
commit
a5fc95f774
@@ -304,6 +304,9 @@ static meshtastic_MeshPacket makeDecodedPacket(meshtastic_PortNum portnum, uint8
|
||||
packet.which_payload_variant = meshtastic_MeshPacket_decoded_tag;
|
||||
packet.decoded.portnum = portnum;
|
||||
packet.channel = channelIndex;
|
||||
// A real destination: this suite never sets a node number, so a default to=0 would read as
|
||||
// "to us" (getNodeNum()==0) and take the from-us-to-us loopback exemption.
|
||||
packet.to = NODENUM_BROADCAST;
|
||||
return packet;
|
||||
}
|
||||
|
||||
@@ -373,7 +376,12 @@ static void test_eventCoordinatePolicy_usesResolvedUnicastChannel()
|
||||
configureEventChannels(false, false);
|
||||
meshtastic_NodeInfoLite *node =
|
||||
nodeDB->getNumMeshNodes() > 1 ? nodeDB->getMeshNodeByIndex(1) : nodeDB->getOrCreateMeshNode(0x12345678);
|
||||
// A persisted DB (unsandboxed host run) can hand back our own entry here; a from-us-to-us packet is
|
||||
// loopback-exempt, which is not the policy under test. Insist on a remote destination.
|
||||
if (node && node->num == nodeDB->getNodeNum())
|
||||
node = nodeDB->getOrCreateMeshNode(0x12345678);
|
||||
TEST_ASSERT_NOT_NULL(node);
|
||||
TEST_ASSERT_NOT_EQUAL(nodeDB->getNodeNum(), node->num);
|
||||
const NodeNum destination = node->num;
|
||||
const uint8_t savedChannel = node->channel;
|
||||
|
||||
@@ -396,6 +404,38 @@ static void test_eventCoordinatePolicy_usesResolvedUnicastChannel()
|
||||
#endif
|
||||
}
|
||||
|
||||
static void test_findPositionChannel_skipsEventAndDisabledChannels()
|
||||
{
|
||||
// Both channels store precision 16. Under the block gate the event channel never carries
|
||||
// positions, so the private one (index 1) is the position channel; otherwise index 0 wins.
|
||||
configureEventChannels(false, false);
|
||||
uint8_t positionChannel = 0xff;
|
||||
TEST_ASSERT_TRUE(findPositionChannel(positionChannel));
|
||||
#if USERPREFS_BLOCK_POSITION_ON_EVENT_CHANNEL && defined(USERPREFS_CHANNEL_0_PSK)
|
||||
TEST_ASSERT_EQUAL_UINT8(1, positionChannel);
|
||||
#else
|
||||
TEST_ASSERT_EQUAL_UINT8(0, positionChannel);
|
||||
#endif
|
||||
|
||||
// Reordering follows the effective key, not the index.
|
||||
configureEventChannels(true, false);
|
||||
TEST_ASSERT_TRUE(findPositionChannel(positionChannel));
|
||||
TEST_ASSERT_EQUAL_UINT8(0, positionChannel);
|
||||
|
||||
// Precision 0 everywhere: nothing to pick.
|
||||
configureEventChannels(false, false);
|
||||
channelFile.channels[0].settings.module_settings.position_precision = 0;
|
||||
channelFile.channels[1].settings.module_settings.position_precision = 0;
|
||||
channels.onConfigChanged();
|
||||
TEST_ASSERT_FALSE(findPositionChannel(positionChannel));
|
||||
|
||||
// A disabled channel does not count even with a stored precision.
|
||||
channelFile.channels[1].settings.module_settings.position_precision = 32;
|
||||
channelFile.channels[1].role = meshtastic_Channel_Role_DISABLED;
|
||||
channels.onConfigChanged();
|
||||
TEST_ASSERT_FALSE(findPositionChannel(positionChannel));
|
||||
}
|
||||
|
||||
static void test_getPositionPrecisionForChannel_nonEventFullKeyIsHonored()
|
||||
{
|
||||
// A private channel with a full 32-byte key that is not the configured
|
||||
@@ -439,6 +479,7 @@ void setup()
|
||||
RUN_TEST(test_eventCoordinatePolicy_coversPortsAndExcludesPki);
|
||||
RUN_TEST(test_eventCoordinatePolicy_doesNotClassifyOpaquePacketsByHash);
|
||||
RUN_TEST(test_eventCoordinatePolicy_usesResolvedUnicastChannel);
|
||||
RUN_TEST(test_findPositionChannel_skipsEventAndDisabledChannels);
|
||||
RUN_TEST(test_getPositionPrecisionForChannel_nonEventFullKeyIsHonored);
|
||||
exit(UNITY_END());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user