Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 16 additions & 7 deletions src/mesh/MeshService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -295,14 +295,16 @@ bool MeshService::cancelSending(PacketId id)
return router->cancelSending(nodeDB->getNodeNum(), id);
}

ErrorCode MeshService::sendQueueStatusToPhone(const meshtastic_QueueStatus &qs, ErrorCode res, uint32_t mesh_packet_id)
ErrorCode MeshService::sendQueueStatusToPhone(const meshtastic_QueueStatus &qs, ErrorCode res, uint32_t mesh_packet_id,
meshtastic_QueueStatus_State state)
{
meshtastic_QueueStatus *copied = queueStatusPool.allocCopy(qs);
if (!copied)
return ERRNO_UNKNOWN;

copied->res = res;
copied->mesh_packet_id = mesh_packet_id;
copied->state = state;

if (toPhoneQueueStatusQueue.numFree() == 0) {
LOG_INFO("tophone queue status queue is full, discard oldest");
Expand All @@ -319,7 +321,7 @@ ErrorCode MeshService::sendQueueStatusToPhone(const meshtastic_QueueStatus &qs,
return res ? ERRNO_OK : ERRNO_UNKNOWN;
}

void MeshService::sendToMesh(meshtastic_MeshPacket *p, RxSource src, bool ccToPhone)
ErrorCode MeshService::sendToMesh(meshtastic_MeshPacket *p, RxSource src, bool ccToPhone, bool reportQueueStatus)
{
uint32_t mesh_packet_id = p->id;
nodeDB->updateFrom(*p); // update our local DB for this packet (because phone might have sent position packets etc...)
Expand All @@ -335,11 +337,16 @@ void MeshService::sendToMesh(meshtastic_MeshPacket *p, RxSource src, bool ccToPh

/* NOTE(pboldin): Prepare and send QueueStatus message to the phone as a
* high-priority message. */
meshtastic_QueueStatus qs = router->getQueueStatus();
// SHOULD_RELEASE means "caller frees", not a send failure, so don't report it as one.
ErrorCode r = sendQueueStatusToPhone(qs, (res == ERRNO_SHOULD_RELEASE && localDelivery) ? ERRNO_OK : res, mesh_packet_id);
if (r != ERRNO_OK) {
LOG_DEBUG("Can't send status to phone");
if (reportQueueStatus) {
meshtastic_QueueStatus qs = router->getQueueStatus();
const auto state = router->isDeferredDm(mesh_packet_id) ? meshtastic_QueueStatus_State_KEY_EXCHANGE
: meshtastic_QueueStatus_State_STATE_UNSPECIFIED;
// SHOULD_RELEASE means "caller frees", not a send failure, so don't report it as one.
ErrorCode r =
sendQueueStatusToPhone(qs, (res == ERRNO_SHOULD_RELEASE && localDelivery) ? ERRNO_OK : res, mesh_packet_id, state);
if (r != ERRNO_OK) {
LOG_DEBUG("Can't send status to phone");
}
}

if ((res == ERRNO_OK || res == ERRNO_SHOULD_RELEASE) && ccToPhone) { // Check if p is not released in case it couldn't be sent
Expand All @@ -355,6 +362,8 @@ void MeshService::sendToMesh(meshtastic_MeshPacket *p, RxSource src, bool ccToPh
if (res == ERRNO_SHOULD_RELEASE) {
releaseToPool(p);
}

return res;
}

bool MeshService::trySendPosition(NodeNum dest, bool wantReplies)
Expand Down
6 changes: 4 additions & 2 deletions src/mesh/MeshService.h
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,8 @@ class MeshService
/// Send a packet into the mesh - note p must have been allocated from packetPool. We will return it to that pool after
/// sending. This is the ONLY function you should use for sending messages into the mesh, because it also updates the nodedb
/// cache
void sendToMesh(meshtastic_MeshPacket *p, RxSource src = RX_SRC_LOCAL, bool ccToPhone = false);
ErrorCode sendToMesh(meshtastic_MeshPacket *p, RxSource src = RX_SRC_LOCAL, bool ccToPhone = false,
bool reportQueueStatus = true);

/** Attempt to cancel a previously sent packet from this _local_ node. Returns true if a packet was found we could cancel */
bool cancelSending(PacketId id);
Expand All @@ -203,7 +204,8 @@ class MeshService

bool isToPhoneQueueEmpty();

ErrorCode sendQueueStatusToPhone(const meshtastic_QueueStatus &qs, ErrorCode res, uint32_t mesh_packet_id);
ErrorCode sendQueueStatusToPhone(const meshtastic_QueueStatus &qs, ErrorCode res, uint32_t mesh_packet_id,
meshtastic_QueueStatus_State state = meshtastic_QueueStatus_State_STATE_UNSPECIFIED);

uint32_t GetTimeSinceMeshPacket(const meshtastic_MeshPacket *mp);

Expand Down
13 changes: 8 additions & 5 deletions src/mesh/NodeDB.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3386,7 +3386,8 @@ bool NodeDB::updateUser(uint32_t nodeId, meshtastic_User &p, uint8_t channelInde
return false;
}
}
if (info->public_key.size == 32) { // if we have a key for this user already, don't overwrite with a new one
if (info->public_key.size == 32 && !memfll(info->public_key.bytes, 0, sizeof(info->public_key.bytes))) {
// If we have a usable key for this user already, don't overwrite it with a new one.
// if the key doesn't match, don't update nodeDB at all.
if (p.public_key.size != 32 || (memcmp(p.public_key.bytes, info->public_key.bytes, 32) != 0)) {
LOG_WARN("Public Key mismatch, dropping NodeInfo");
Expand Down Expand Up @@ -3760,12 +3761,12 @@ uint32_t NodeDB::hotNodeLastHeard(NodeNum n) const
bool NodeDB::copyPublicKeyAuthoritative(NodeNum n, meshtastic_NodeInfoLite_public_key_t &out)
{
const meshtastic_NodeInfoLite *info = getMeshNode(n);
if (info && info->public_key.size == 32) {
if (info && info->public_key.size == 32 && !memfll(info->public_key.bytes, 0, sizeof(info->public_key.bytes))) {
out = info->public_key;
return true;
}
#if WARM_NODE_COUNT > 0
if (warmStore.copyKey(n, out.bytes)) {
if (warmStore.copyKey(n, out.bytes) && !memfll(out.bytes, 0, sizeof(out.bytes))) {
out.size = 32;
return true;
}
Expand All @@ -3782,7 +3783,8 @@ bool NodeDB::copyPublicKey(NodeNum n, meshtastic_NodeInfoLite_public_key_t &out)
// for a node no longer in either NodeDB tier. This extends the pool of peers we can
// encrypt to. Keys here may be trust-on-first-use (see copyPublicKey's signerProven), the
// same first-contact trust NodeDB itself applies via updateUser().
if (trafficManagementModule && trafficManagementModule->copyPublicKey(n, out.bytes)) {
if (trafficManagementModule && trafficManagementModule->copyPublicKey(n, out.bytes) &&
!memfll(out.bytes, 0, sizeof(out.bytes))) {
out.size = 32;
return true;
}
Expand All @@ -3798,7 +3800,8 @@ bool NodeDB::copyPublicKeyForDecrypt(NodeNum n, meshtastic_NodeInfoLite_public_k
// A cold-tier cache key backs an authenticated decrypt only when signer-proven; unverified TOFU
// cache keys must not. Outbound encryption still uses the opportunistic copyPublicKey().
bool signerProven = false;
if (trafficManagementModule && trafficManagementModule->copyPublicKey(n, out.bytes, &signerProven) && signerProven) {
if (trafficManagementModule && trafficManagementModule->copyPublicKey(n, out.bytes, &signerProven) && signerProven &&
!memfll(out.bytes, 0, sizeof(out.bytes))) {
out.size = 32;
return true;
}
Expand Down
6 changes: 5 additions & 1 deletion src/mesh/PhoneAPI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1775,6 +1775,10 @@ bool PhoneAPI::handleToRadioPacket(meshtastic_MeshPacket &p)
#endif
if (p.id > 0 && wasSeenRecently(p.id)) {
LOG_DEBUG("Ignore packet from phone, already seen recently");
if (router->isDeferredDm(p.id)) {
meshtastic_QueueStatus qs = router->getQueueStatus();
service->sendQueueStatusToPhone(qs, ERRNO_OK, p.id, meshtastic_QueueStatus_State_KEY_EXCHANGE);
}
return false;
}

Expand Down Expand Up @@ -1805,7 +1809,7 @@ bool PhoneAPI::handleToRadioPacket(meshtastic_MeshPacket &p)
Throttle::isWithinTimespanMs(lastPortNumToRadio[p.decoded.portnum], TWO_SECONDS_MS)) {
LOG_WARN("Rate limit portnum %d", p.decoded.portnum);
meshtastic_QueueStatus qs = router->getQueueStatus();
service->sendQueueStatusToPhone(qs, 0, p.id);
service->sendQueueStatusToPhone(qs, meshtastic_Routing_Error_RATE_LIMIT_EXCEEDED, p.id);
service->sendRoutingErrorResponse(meshtastic_Routing_Error_RATE_LIMIT_EXCEEDED, &p);
// sendNotification(meshtastic_LogRecord_Level_WARNING, p.id, "Text messages can only be sent once every 2 seconds");
return false;
Expand Down
3 changes: 1 addition & 2 deletions src/mesh/RadioInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -174,8 +174,7 @@ class RadioInterface
/** Return TX queue status */
[[nodiscard]] virtual meshtastic_QueueStatus getQueueStatus()
{
meshtastic_QueueStatus qs;
qs.res = qs.mesh_packet_id = qs.free = qs.maxlen = 0;
meshtastic_QueueStatus qs = meshtastic_QueueStatus_init_zero;
return qs;
}

Expand Down
2 changes: 1 addition & 1 deletion src/mesh/RadioLibInterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ ErrorCode RadioLibInterface::send(meshtastic_MeshPacket *p)

meshtastic_QueueStatus RadioLibInterface::getQueueStatus()
{
meshtastic_QueueStatus qs;
meshtastic_QueueStatus qs = meshtastic_QueueStatus_init_zero;

qs.res = qs.mesh_packet_id = 0;
qs.free = txQueue.getFree();
Expand Down
62 changes: 54 additions & 8 deletions src/mesh/ReliableRouter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,26 @@
*/
ErrorCode ReliableRouter::send(meshtastic_MeshPacket *p)
{
#if !MESHTASTIC_EXCLUDE_PKI && !MESHTASTIC_EXCLUDE_NODEINFO
// Router owns delayed DMs before creating retransmission state, otherwise a stale copy can
// later emit MAX_RETRANSMIT. First request that a peer refresh our NodeInfo, then recover a
// missing destination key when necessary.
auto deferredDm = deferPeerKeyDm(p, false);
if (deferredDm == DeferredDmResult::DEFERRED)
return ERRNO_OK;
if (deferredDm == DeferredDmResult::FAILED) {
abortSendAndNak(meshtastic_Routing_Error_PKI_SEND_FAIL_PUBLIC_KEY, p);
return meshtastic_Routing_Error_PKI_SEND_FAIL_PUBLIC_KEY;
}
deferredDm = deferMissingKeyDm(p);
if (deferredDm == DeferredDmResult::DEFERRED)
return ERRNO_OK;
if (deferredDm == DeferredDmResult::FAILED) {
abortSendAndNak(meshtastic_Routing_Error_PKI_SEND_FAIL_PUBLIC_KEY, p);
return meshtastic_Routing_Error_PKI_SEND_FAIL_PUBLIC_KEY;
}
#endif

if (p->want_ack) {
DEBUG_HEAP_BEFORE;
auto copy = packetPool.allocCopy(*p);
Expand Down Expand Up @@ -93,6 +113,29 @@ bool ReliableRouter::shouldFilterReceived(const meshtastic_MeshPacket *p)
void ReliableRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtastic_Routing *c)
{
if (isToUs(p)) { // ignore ack/nak/want_ack packets that are not address to us (we only handle 0 hop reliability)
bool deferredForPeerKey = false;
bool alreadyRetriedForPeerKey = false;
if (c && c->error_reason == meshtastic_Routing_Error_PKI_UNKNOWN_PUBKEY &&
p->which_payload_variant == meshtastic_MeshPacket_decoded_tag && p->decoded.request_id) {
#if !MESHTASTIC_EXCLUDE_PKI && !MESHTASTIC_EXCLUDE_NODEINFO
if (isWaitingForPeerKeyDm(p->from, p->decoded.request_id)) {
suppressRoutingDelivery(*p);
deferredForPeerKey = true;
} else if (!(alreadyRetriedForPeerKey = hasRetriedPeerKeyDm(p->from, p->decoded.request_id))) {
if (PendingPacket *pendingPacket = findPendingPacket(GlobalPacketId(p->to, p->decoded.request_id))) {
meshtastic_MeshPacket *retry = packetPool.allocCopy(*pendingPacket->packet);
if (retry && deferPeerKeyDm(retry, true, true) == DeferredDmResult::DEFERRED) {
rememberPeerKeyRetry(p->from, p->decoded.request_id);
stopRetransmission(p->to, p->decoded.request_id);
suppressRoutingDelivery(*p);
deferredForPeerKey = true;
} else if (retry) {
packetPool.release(retry);
}
}
}
#endif
}
if (!MeshModule::currentReply) {
if (p->want_ack) {
if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
Expand All @@ -115,11 +158,14 @@ void ReliableRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtas
// stop the immediate relayer's retransmissions.
sendAckNak(meshtastic_Routing_Error_NONE, getFrom(p), p->id, p->channel, 0);
}
} else if (p->which_payload_variant == meshtastic_MeshPacket_encrypted_tag && p->channel == 0 &&
(nodeDB->getMeshNode(p->from) == nullptr || nodeDB->getMeshNode(p->from)->public_key.size == 0)) {
LOG_INFO("PKI packet from unknown node, send PKI_UNKNOWN_PUBKEY");
sendAckNak(meshtastic_Routing_Error_PKI_UNKNOWN_PUBKEY, getFrom(p), p->id, channels.getPrimaryIndex(),
routingModule->getHopLimitForResponse(*p));
} else if (p->which_payload_variant == meshtastic_MeshPacket_encrypted_tag && p->channel == 0) {
const meshtastic_NodeInfoLite *sender = nodeDB->getMeshNode(p->from);
const bool hasSenderKey = sender && sender->public_key.size == 32 &&
!memfll(sender->public_key.bytes, 0, sizeof(sender->public_key.bytes));
const auto error =
hasSenderKey ? meshtastic_Routing_Error_PKI_FAILED : meshtastic_Routing_Error_PKI_UNKNOWN_PUBKEY;
LOG_INFO("Undecryptable PKI packet from 0x%08x, send error %d", p->from, error);
sendAckNak(error, getFrom(p), p->id, channels.getPrimaryIndex(), routingModule->getHopLimitForResponse(*p));
} else {
// Send a 'NO_CHANNEL' error on the primary channel if want_ack packet destined for us cannot be decoded
sendAckNak(meshtastic_Routing_Error_NO_CHANNEL, getFrom(p), p->id, channels.getPrimaryIndex(),
Expand All @@ -132,8 +178,8 @@ void ReliableRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtas
} else {
LOG_DEBUG("Another module replied to this message, no need for 2nd ack");
}
if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag && c &&
c->error_reason == meshtastic_Routing_Error_PKI_UNKNOWN_PUBKEY) {
if (!deferredForPeerKey && !alreadyRetriedForPeerKey && p->which_payload_variant == meshtastic_MeshPacket_decoded_tag &&
c && c->error_reason == meshtastic_Routing_Error_PKI_UNKNOWN_PUBKEY) {
if (owner.public_key.size == 32) {
LOG_INFO("PKI decrypt failure, send a NodeInfo");
nodeInfoModule->sendOurNodeInfo(p->from, false, p->channel, true);
Expand Down Expand Up @@ -196,4 +242,4 @@ bool ReliableRouter::shouldSuccessAckWithWantAck(const meshtastic_MeshPacket *p)
}

return false;
}
}
Loading
Loading