Block coordinate traffic on configured event channels (#11045)

* Block coordinate traffic on configured event channels

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* Suppress event coordinates in reliable relay paths

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* Reject blocked phone coordinates before rate limiting

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* Prevent event coordinates from reaching MQTT

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* Add event coordinate policy preference

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* Test event coordinate policy in native CI

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* Make event policy test tolerate a full NodeDB

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* Test Router event coordinate enforcement

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* Test PhoneAPI event coordinate retry handling

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* Test reliable event coordinate suppression

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* Test MQTT event coordinate suppression

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* Run event policy behavioral suites in native CI

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* tests: address CodeRabbit review feedback

- test_event_channel_phone_api: complete the setUp/tearDown save-restore
  pair. GlobalState now carries cryptLock and myNodeInfo; setUp() nulls
  cryptLock before constructing MockRouter (Router's ctor asserts it is
  unset), and tearDown() restores both so the suite leaves no global
  mutated. Not reachable today - the globals start null in this binary -
  but the pair was asymmetric.

- Replace the strcpy calls this branch added on Channel.settings.name
  (char[12]) with the bounded form the rest of the test tree already uses,
  strncpy(dst, src, sizeof(dst) - 1). Covers the flagged site in
  test_nexthop_routing plus the six equivalents in
  test_event_channel_phone_api, test_mqtt and test_position_precision,
  which trip the same ast-grep dangerous-buffer-functions-cpp rule.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
ayysasha
2026-08-11 12:34:08 +00:00
committed by GitHub
co-authored by Sisyphus Claude Opus 5 Ben Meadors
parent fb17f6ddbe
commit 546b9d9e40
18 changed files with 1492 additions and 36 deletions
+70 -4
View File
@@ -69,6 +69,52 @@ Allocator<meshtastic_MeshPacket> &packetPool = staticPool;
static uint8_t bytes[MAX_LORA_PAYLOAD_LEN + 1] __attribute__((__aligned__));
static ChannelIndex getEffectiveChannelIndex(const meshtastic_MeshPacket *p)
{
ChannelIndex chIndex = p->channel;
if (nodeDB && isFromUs(p) && !chIndex && !p->pki_encrypted && !isBroadcast(p->to)) {
const meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(p->to);
if (node)
chIndex = node->channel;
}
return chIndex;
}
bool isBlockedEventCoordinatePacket(const meshtastic_MeshPacket *p)
{
#if USERPREFS_BLOCK_POSITION_ON_EVENT_CHANNEL
if (p->pki_encrypted || willUsePki(p)) {
return false;
}
if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
return isCoordinatePortnum(p->decoded.portnum) && channels.isEventChannel(getEffectiveChannelIndex(p));
}
return false;
#else
(void)p;
return false;
#endif
}
bool willUsePki(const meshtastic_MeshPacket *p)
{
#if !(MESHTASTIC_EXCLUDE_PKI)
if (p->which_payload_variant != meshtastic_MeshPacket_decoded_tag || !isFromUs(p))
return false;
bool haveDestKey = false;
if (p->decoded.portnum == meshtastic_PortNum_KEY_VERIFICATION_APP) {
meshtastic_NodeInfoLite_public_key_t destKey = {0, {0}};
haveDestKey = nodeDB->copyPublicKey(p->to, destKey);
if (!haveDestKey && p->pki_encrypted)
haveDestKey = crypto->getPendingPublicKey(p->to, destKey);
}
return wouldEncryptWithPKC(p, getEffectiveChannelIndex(p), haveDestKey);
#else
(void)p;
return false;
#endif
}
struct RoutingAuthCache {
bool valid = false;
// Deliberately NOT initialized in-class as this eats flash space.
@@ -141,7 +187,6 @@ void resetRoutingAuthEvaluationCount()
}
}
#endif
/**
* Constructor
*
@@ -360,9 +405,9 @@ ErrorCode Router::sendLocal(meshtastic_MeshPacket *p, RxSource src)
// don't override if a channel was requested and no need to set it when PKI is enforced
if (!p->channel && !p->pki_encrypted && !isBroadcast(p->to)) {
meshtastic_NodeInfoLite const *node = nodeDB->getMeshNode(p->to);
if (node) {
p->channel = node->channel;
ChannelIndex chIndex = getEffectiveChannelIndex(p);
if (chIndex) {
p->channel = chIndex;
LOG_DEBUG("localSend to channel %d", p->channel);
}
}
@@ -473,6 +518,12 @@ ErrorCode Router::send(meshtastic_MeshPacket *p)
fixPriority(p); // Before encryption, fix the priority if it's unset
// Position precision is an originator-only privacy policy. Relays keep
// p->from as the original sender, so do not rewrite their POSITION_APP payload.
if (isBlockedEventCoordinatePacket(p)) {
LOG_DEBUG("Suppress coordinate send on event (everyone) channel");
packetPool.release(p);
return meshtastic_Routing_Error_NOT_AUTHORIZED;
}
if (isFromUs(p)) {
if (!applyPositionPrecisionForChannel(*p, p->channel)) {
LOG_ERROR("Drop malformed position packet before send");
@@ -959,6 +1010,11 @@ DecodeState perhapsDecode(meshtastic_MeshPacket *p)
return DecodeState::DECODE_POLICY_REJECT;
#endif
if (isBlockedEventCoordinatePacket(p)) {
LOG_DEBUG("Decoded coordinate packet on event channel; suppress payload logging");
return DecodeState::DECODE_SUCCESS;
}
if (p->decoded.has_bitfield)
p->decoded.want_response |= p->decoded.bitfield & BITFIELD_WANT_RESPONSE_MASK;
@@ -1436,6 +1492,16 @@ void Router::dispatchReceived(meshtastic_MeshPacket *p, RxSource src)
cancelSending(p->from, p->id);
skipHandle = true;
}
#if USERPREFS_BLOCK_POSITION_ON_EVENT_CHANNEL
// Discard coordinate-bearing packets that arrive on the event ("everyone")
// channel: don't process, store in NodeDB, or rebroadcast them.
if (!skipHandle && isBlockedEventCoordinatePacket(p)) {
LOG_DEBUG("Drop coordinate packet on event (everyone) channel");
cancelSending(p->from, p->id);
skipHandle = true;
}
#endif
} else {
printPacket("packet decoding failed or skipped (no PSK?)", p);
}