diff --git a/src/helpers/BaseChatMesh.cpp b/src/helpers/BaseChatMesh.cpp index 33d7edbee4..5896f7704c 100644 --- a/src/helpers/BaseChatMesh.cpp +++ b/src/helpers/BaseChatMesh.cpp @@ -1,5 +1,6 @@ #include #include +#include #ifndef SERVER_RESPONSE_DELAY #define SERVER_RESPONSE_DELAY 300 @@ -39,7 +40,7 @@ mesh::Packet* BaseChatMesh::createSelfAdvert(const char* name, double lat, doubl } void BaseChatMesh::sendAckTo(const ContactInfo& dest, uint32_t ack_hash) { - if (dest.out_path_len == OUT_PATH_UNKNOWN) { + if (!shouldUseDirectPath(dest)) { mesh::Packet* ack = createAck(ack_hash); if (ack) sendFloodScoped(dest, ack, TXT_ACK_DELAY); } else { @@ -55,6 +56,63 @@ void BaseChatMesh::sendAckTo(const ContactInfo& dest, uint32_t ack_hash) { } } +bool BaseChatMesh::isAmbiguousDirectPath(const ContactInfo& recipient) const { + if (recipient.out_path_len == OUT_PATH_UNKNOWN || recipient.out_path_len == 0) { + return false; + } + if (recipient.out_path_len > MAX_PATH_SIZE) return true; + + for (int i = 0; i < recipient.out_path_len; i++) { + const uint8_t hop = recipient.out_path[i]; + int matches = 0; + for (int j = 0; j < num_contacts; j++) { + if (contacts[j].id.pub_key[0] == hop) { + if (++matches >= 2) return true; + } + } + } + return false; +} + +bool BaseChatMesh::isTrackedContact(const ContactInfo& recipient, int& idx) const { + if (num_contacts <= 0) return false; + + const uintptr_t start = (uintptr_t) &contacts[0]; + const uintptr_t end = (uintptr_t) (&contacts[num_contacts - 1]) + sizeof(ContactInfo); + const uintptr_t p = (uintptr_t) &recipient; + if (p < start || p >= end) return false; + if (((p - start) % sizeof(ContactInfo)) != 0) return false; + + idx = (int) ((p - start) / sizeof(ContactInfo)); + if (idx < 0 || idx >= num_contacts) return false; + if (&contacts[idx] != &recipient) return false; + + return true; +} + +void BaseChatMesh::updateDirectPathAmbiguousAt(int idx) { + if (idx < 0 || idx >= num_contacts) return; + direct_path_ambiguous[idx] = isAmbiguousDirectPath(contacts[idx]); +} + +void BaseChatMesh::updateAllDirectPathAmbiguous() { + for (int i = 0; i < num_contacts; i++) { + direct_path_ambiguous[i] = isAmbiguousDirectPath(contacts[i]); + } +} + +bool BaseChatMesh::shouldUseDirectPath(const ContactInfo& recipient) const { + if (recipient.out_path_len == OUT_PATH_UNKNOWN) return false; + if (recipient.out_path_len > MAX_PATH_SIZE) return false; + + int idx; + if (isTrackedContact(recipient, idx)) { + return !direct_path_ambiguous[idx]; + } + + return !isAmbiguousDirectPath(recipient); +} + void BaseChatMesh::bootstrapRTCfromContacts() { uint32_t latest = 0; for (int i = 0; i < num_contacts; i++) { @@ -163,6 +221,8 @@ void BaseChatMesh::onAdvertRecv(mesh::Packet* packet, const mesh::Identity& id, populateContactFromAdvert(*from, id, parser, timestamp); from->sync_since = 0; from->shared_secret_valid = false; + is_new = true; + updateAllDirectPathAmbiguous(); } // update putBlobByKey(id.pub_key, PUB_KEY_SIZE, temp_buf, plen); @@ -272,7 +332,7 @@ void BaseChatMesh::onPeerDataRecv(mesh::Packet* packet, uint8_t type, int sender } else { mesh::Packet* reply = createDatagram(PAYLOAD_TYPE_RESPONSE, from.id, secret, temp_buf, reply_len); if (reply) { - if (from.out_path_len != OUT_PATH_UNKNOWN) { // we have an out_path, so send DIRECT + if (shouldUseDirectPath(from)) { // we have an unambiguous out_path, so send DIRECT sendDirect(reply, from.out_path, from.out_path_len, SERVER_RESPONSE_DELAY); } else { sendFloodScoped(from, reply, SERVER_RESPONSE_DELAY); @@ -282,7 +342,7 @@ void BaseChatMesh::onPeerDataRecv(mesh::Packet* packet, uint8_t type, int sender } } else if (type == PAYLOAD_TYPE_RESPONSE && len > 0) { onContactResponse(from, data, len); - if (packet->isRouteFlood() && from.out_path_len != OUT_PATH_UNKNOWN) { + if (packet->isRouteFlood() && shouldUseDirectPath(from)) { // we have direct path, but other node is still sending flood response, so maybe they didn't receive reciprocal path properly(?) handleReturnPathRetry(from, packet->path, packet->path_len); } @@ -305,6 +365,12 @@ bool BaseChatMesh::onContactPathRecv(ContactInfo& from, uint8_t* in_path, uint8_ // NOTE: default impl, we just replace the current 'out_path' regardless, whenever sender sends us a new out_path. // FUTURE: could store multiple out_paths per contact, and try to find which is the 'best'(?) from.out_path_len = mesh::Packet::copyPath(from.out_path, out_path, out_path_len); // store a copy of path, for sendDirect() + { + int idx; + if (isTrackedContact(from, idx)) { + updateDirectPathAmbiguousAt(idx); + } + } from.lastmod = getRTCClock()->getCurrentTime(); onContactPathUpdated(from); @@ -326,7 +392,7 @@ void BaseChatMesh::onAckRecv(mesh::Packet* packet, uint32_t ack_crc) { txt_send_timeout = 0; // matched one we're waiting for, cancel timeout timer packet->markDoNotRetransmit(); // ACK was for this node, so don't retransmit - if (packet->isRouteFlood() && from->out_path_len != OUT_PATH_UNKNOWN) { + if (packet->isRouteFlood() && shouldUseDirectPath(*from)) { // we have direct path, but other node is still sending flood, so maybe they didn't receive reciprocal path properly(?) handleReturnPathRetry(*from, packet->path, packet->path_len); } @@ -336,6 +402,7 @@ void BaseChatMesh::onAckRecv(mesh::Packet* packet, uint32_t ack_crc) { void BaseChatMesh::handleReturnPathRetry(const ContactInfo& contact, const uint8_t* path, uint8_t path_len) { // NOTE: simplest impl is just to re-send a reciprocal return path to sender (DIRECTLY) // override this method in various firmwares, if there's a better strategy + if (!shouldUseDirectPath(contact)) return; mesh::Packet* rpath = createPathReturn(contact.id, contact.getSharedSecret(self_id), path, path_len, 0, NULL, 0); if (rpath) sendDirect(rpath, contact.out_path, contact.out_path_len, 3000); // 3 second delay } @@ -395,7 +462,7 @@ int BaseChatMesh::sendMessage(const ContactInfo& recipient, uint32_t timestamp, uint32_t t = _radio->getEstAirtimeFor(pkt->getRawLength()); int rc; - if (recipient.out_path_len == OUT_PATH_UNKNOWN) { + if (!shouldUseDirectPath(recipient)) { sendFloodScoped(recipient, pkt); txt_send_timeout = futureMillis(est_timeout = calcFloodTimeoutMillisFor(t)); rc = MSG_SEND_SENT_FLOOD; @@ -421,7 +488,7 @@ int BaseChatMesh::sendCommandData(const ContactInfo& recipient, uint32_t timest uint32_t t = _radio->getEstAirtimeFor(pkt->getRawLength()); int rc; - if (recipient.out_path_len == OUT_PATH_UNKNOWN) { + if (!shouldUseDirectPath(recipient)) { sendFloodScoped(recipient, pkt); txt_send_timeout = futureMillis(est_timeout = calcFloodTimeoutMillisFor(t)); rc = MSG_SEND_SENT_FLOOD; @@ -509,7 +576,7 @@ int BaseChatMesh::sendLogin(const ContactInfo& recipient, const char* password, } if (pkt) { uint32_t t = _radio->getEstAirtimeFor(pkt->getRawLength()); - if (recipient.out_path_len == OUT_PATH_UNKNOWN) { + if (!shouldUseDirectPath(recipient)) { sendFloodScoped(recipient, pkt); est_timeout = calcFloodTimeoutMillisFor(t); return MSG_SEND_SENT_FLOOD; @@ -534,7 +601,7 @@ int BaseChatMesh::sendAnonReq(const ContactInfo& recipient, const uint8_t* data, } if (pkt) { uint32_t t = _radio->getEstAirtimeFor(pkt->getRawLength()); - if (recipient.out_path_len == OUT_PATH_UNKNOWN) { + if (!shouldUseDirectPath(recipient)) { sendFloodScoped(recipient, pkt); est_timeout = calcFloodTimeoutMillisFor(t); return MSG_SEND_SENT_FLOOD; @@ -561,7 +628,7 @@ int BaseChatMesh::sendRequest(const ContactInfo& recipient, const uint8_t* req_ } if (pkt) { uint32_t t = _radio->getEstAirtimeFor(pkt->getRawLength()); - if (recipient.out_path_len == OUT_PATH_UNKNOWN) { + if (!shouldUseDirectPath(recipient)) { sendFloodScoped(recipient, pkt); est_timeout = calcFloodTimeoutMillisFor(t); return MSG_SEND_SENT_FLOOD; @@ -588,7 +655,7 @@ int BaseChatMesh::sendRequest(const ContactInfo& recipient, uint8_t req_type, u } if (pkt) { uint32_t t = _radio->getEstAirtimeFor(pkt->getRawLength()); - if (recipient.out_path_len == OUT_PATH_UNKNOWN) { + if (!shouldUseDirectPath(recipient)) { sendFloodScoped(recipient, pkt); est_timeout = calcFloodTimeoutMillisFor(t); return MSG_SEND_SENT_FLOOD; @@ -692,8 +759,8 @@ void BaseChatMesh::checkConnections() { MESH_DEBUG_PRINTLN("checkConnections(): Keep_alive contact not found!"); continue; } - if (contact->out_path_len == OUT_PATH_UNKNOWN) { - MESH_DEBUG_PRINTLN("checkConnections(): Keep_alive contact, no out_path!"); + if (!shouldUseDirectPath(*contact)) { + MESH_DEBUG_PRINTLN("checkConnections(): Keep_alive contact, no direct path candidate"); continue; } @@ -720,6 +787,10 @@ void BaseChatMesh::checkConnections() { void BaseChatMesh::resetPathTo(ContactInfo& recipient) { recipient.out_path_len = OUT_PATH_UNKNOWN; + int idx; + if (isTrackedContact(recipient, idx)) { + direct_path_ambiguous[idx] = false; + } } static ContactInfo* table; // pass via global :-( @@ -771,6 +842,7 @@ bool BaseChatMesh::addContact(const ContactInfo& contact) { if (dest) { *dest = contact; dest->shared_secret_valid = false; // mark shared_secret as needing calculation + updateAllDirectPathAmbiguous(); return true; // success } return false; @@ -789,6 +861,10 @@ bool BaseChatMesh::removeContact(ContactInfo& contact) { contacts[idx] = contacts[idx + 1]; idx++; } + if (num_contacts < MAX_CONTACTS) { + direct_path_ambiguous[num_contacts] = false; + } + updateAllDirectPathAmbiguous(); return true; // Success } diff --git a/src/helpers/BaseChatMesh.h b/src/helpers/BaseChatMesh.h index ab90d581be..2e42d35250 100644 --- a/src/helpers/BaseChatMesh.h +++ b/src/helpers/BaseChatMesh.h @@ -70,8 +70,14 @@ class BaseChatMesh : public mesh::Mesh { mesh::Packet* _pendingLoopback; uint8_t temp_buf[MAX_TRANS_UNIT]; ConnectionInfo connections[MAX_CONNECTIONS]; + bool direct_path_ambiguous[MAX_CONTACTS]; mesh::Packet* composeMsgPacket(const ContactInfo& recipient, uint32_t timestamp, uint8_t attempt, const char *text, uint32_t& expected_ack); + bool isAmbiguousDirectPath(const ContactInfo& recipient) const; + bool isTrackedContact(const ContactInfo& recipient, int& idx) const; + void updateDirectPathAmbiguousAt(int idx); + void updateAllDirectPathAmbiguous(); + bool shouldUseDirectPath(const ContactInfo& recipient) const; void sendAckTo(const ContactInfo& dest, uint32_t ack_hash); protected: @@ -86,10 +92,14 @@ class BaseChatMesh : public mesh::Mesh { txt_send_timeout = 0; _pendingLoopback = NULL; memset(connections, 0, sizeof(connections)); + memset(direct_path_ambiguous, 0, sizeof(direct_path_ambiguous)); } void bootstrapRTCfromContacts(); - void resetContacts() { num_contacts = 0; } + void resetContacts() { + num_contacts = 0; + memset(direct_path_ambiguous, 0, sizeof(direct_path_ambiguous)); + } void populateContactFromAdvert(ContactInfo& ci, const mesh::Identity& id, const AdvertDataParser& parser, uint32_t timestamp); ContactInfo* allocateContactSlot(); // helper to find slot for new contact