Skip to content
Closed
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
100 changes: 88 additions & 12 deletions src/helpers/BaseChatMesh.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include <helpers/BaseChatMesh.h>
#include <Utils.h>
#include <stdint.h>

#ifndef SERVER_RESPONSE_DELAY
#define SERVER_RESPONSE_DELAY 300
Expand Down Expand Up @@ -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 {
Expand All @@ -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++) {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand All @@ -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);
}
Expand All @@ -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);
Expand All @@ -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);
}
Expand All @@ -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
}
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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;
}

Expand All @@ -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 :-(
Expand Down Expand Up @@ -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;
Expand All @@ -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
}

Expand Down
12 changes: 11 additions & 1 deletion src/helpers/BaseChatMesh.h
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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

Expand Down