diff --git a/docs/software/mesh-alg.md b/docs/software/mesh-alg.md index 393abce6..19e7e506 100644 --- a/docs/software/mesh-alg.md +++ b/docs/software/mesh-alg.md @@ -1,12 +1,10 @@ # Mesh broadcast algorithm -FIXME - instead look for standard solutions. this approach seems really suboptimal, because too many nodes will try to rebroast. If -all else fails could always use the stock Radiohead solution - though super inefficient. - great source of papers and class notes: http://www.cs.jhu.edu/~cs647/ reliable messaging tasks (stage one for DSR): +- fix FIXME - should snoop packet not sent to us - add a 'messagePeek' hook for all messages that pass through our node. - DONE use the same 'recentmessages' array used for broadcast msgs to detect duplicate retransmitted messages. - keep possible retries in the list with to be rebroadcast messages? @@ -14,7 +12,6 @@ reliable messaging tasks (stage one for DSR): - delay some random time for each retry (large enough to allow for acks to come in) - once an ack comes in, remove the packet from the retry list and deliver the ack to the original sender - after three retries, deliver a no-ack packet to the original sender (i.e. the phone app or mesh router service) -- add a max hops parameter, use it for broadcast as well (0 means adjacent only, 1 is one forward etc...). Store as three bits in the header. dsr tasks @@ -55,6 +52,8 @@ when we receive a routeError packet TODO: +- optimize our generalized flooding with heuristics, possibly have particular nodes self mark as 'router' nodes. + - DONE reread the radiohead mesh implementation - hop to hop acknowledgement seems VERY expensive but otherwise it seems like DSR - DONE read about mesh routing solutions (DSR and AODV) - DONE read about general mesh flooding solutions (naive, MPR, geo assisted) @@ -62,6 +61,7 @@ TODO: - REJECTED - seems dying - possibly dash7? https://www.slideshare.net/MaartenWeyn1/dash7-alliance-protocol-technical-presentation https://github.com/MOSAIC-LoPoW/dash7-ap-open-source-stack - does the opensource stack implement multihop routing? flooding? their discussion mailing list looks dead-dead - update duty cycle spreadsheet for our typical usecase - DONE generalize naive flooding +- DONE add a max hops parameter, use it for broadcast as well (0 means adjacent only, 1 is one forward etc...). Store as three bits in the header. a description of DSR: https://tools.ietf.org/html/rfc4728 good slides here: https://www.slideshare.net/ashrafmath/dynamic-source-routing good description of batman protocol: https://www.open-mesh.org/projects/open-mesh/wiki/BATMANConcept diff --git a/proto b/proto index bc3ecd97..5799cb10 160000 --- a/proto +++ b/proto @@ -1 +1 @@ -Subproject commit bc3ecd97e381b724c1a28acce0d12c688de73ba3 +Subproject commit 5799cb10b8f3cf353e7791d0609002cc93d9d13d diff --git a/src/mesh/FloodingRouter.cpp b/src/mesh/FloodingRouter.cpp index 6db03dd4..e9941fb1 100644 --- a/src/mesh/FloodingRouter.cpp +++ b/src/mesh/FloodingRouter.cpp @@ -2,8 +2,6 @@ #include "configuration.h" #include "mesh-pb-constants.h" -static bool supportFlooding = true; // Sometimes to simplify debugging we want jusT simple broadcast only - FloodingRouter::FloodingRouter() : toResend(MAX_NUM_NODES) {} /** @@ -13,9 +11,7 @@ FloodingRouter::FloodingRouter() : toResend(MAX_NUM_NODES) {} */ ErrorCode FloodingRouter::send(MeshPacket *p) { - // We update our table of recent broadcasts, even for messages we send - if (supportFlooding) - wasSeenRecently(p); + wasSeenRecently(p); return Router::send(p); } @@ -29,28 +25,28 @@ ErrorCode FloodingRouter::send(MeshPacket *p) */ void FloodingRouter::handleReceived(MeshPacket *p) { - if (supportFlooding) { - if (wasSeenRecently(p)) { - DEBUG_MSG("Ignoring incoming floodmsg, because we've already seen it\n"); - packetPool.release(p); - } else { - if (p->to == NODENUM_BROADCAST) { - if (p->id != 0) { - MeshPacket *tosend = packetPool.allocCopy(*p); // keep a copy because we will be sending it + if (wasSeenRecently(p)) { + DEBUG_MSG("Ignoring incoming msg, because we've already seen it\n"); + packetPool.release(p); + } else { + if (p->to == NODENUM_BROADCAST && p->hop_limit > 0) { + if (p->id != 0) { + MeshPacket *tosend = packetPool.allocCopy(*p); // keep a copy because we will be sending it - DEBUG_MSG("Rebroadcasting received floodmsg to neighbors, fr=0x%x,to=0x%x,id=%d\n", p->from, p->to, p->id); - // Note: we are careful to resend using the original senders node id - // We are careful not to call our hooked version of send() - because we don't want to check this again - Router::send(tosend); + tosend->hop_limit--; // bump down the hop count - } else { - DEBUG_MSG("Ignoring a simple (0 hop) broadcast\n"); - } + DEBUG_MSG("Rebroadcasting received floodmsg to neighbors, fr=0x%x,to=0x%x,id=%d,hop_limit=%d\n", p->from, p->to, + p->id, tosend->hop_limit); + // Note: we are careful to resend using the original senders node id + // We are careful not to call our hooked version of send() - because we don't want to check this again + Router::send(tosend); + + } else { + DEBUG_MSG("Ignoring a simple (0 id) broadcast\n"); } - - // handle the packet as normal - Router::handleReceived(p); } - } else + + // handle the packet as normal Router::handleReceived(p); + } } diff --git a/src/mesh/MeshService.cpp b/src/mesh/MeshService.cpp index 5f4b51ba..28aba7fe 100644 --- a/src/mesh/MeshService.cpp +++ b/src/mesh/MeshService.cpp @@ -285,6 +285,7 @@ MeshPacket *MeshService::allocForSending() p->which_payload = MeshPacket_decoded_tag; // Assume payload is decoded at start. p->from = nodeDB.getNodeNum(); p->to = NODENUM_BROADCAST; + p->hop_limit = HOP_MAX; p->id = generatePacketId(); p->rx_time = getValidTime(); // Just in case we process the packet locally - make sure it has a valid timestamp diff --git a/src/mesh/MeshTypes.h b/src/mesh/MeshTypes.h index 0d9783e1..04bb13ad 100644 --- a/src/mesh/MeshTypes.h +++ b/src/mesh/MeshTypes.h @@ -14,6 +14,15 @@ typedef uint8_t PacketId; // A packet sequence number #define ERRNO_NO_INTERFACES 33 #define ERRNO_UNKNOWN 32 // pick something that doesn't conflict with RH_ROUTER_ERROR_UNABLE_TO_DELIVER +/** + * the max number of hops a message can pass through, used as the default max for hop_limit in MeshPacket. + * + * We reserve 3 bits in the header so this could be up to 7, but given the high range of lora and typical usecases, keeping + * maxhops to 3 should be fine for a while. This also serves to prevent routing/flooding attempts to be attempted for + * too long. + **/ +#define HOP_MAX 3 + typedef int ErrorCode; /// Alloc and free packets to our global, ISR safe pool diff --git a/src/mesh/RadioInterface.cpp b/src/mesh/RadioInterface.cpp index 3ad60005..123e128a 100644 --- a/src/mesh/RadioInterface.cpp +++ b/src/mesh/RadioInterface.cpp @@ -115,8 +115,9 @@ size_t RadioInterface::beginSending(MeshPacket *p) h->from = p->from; h->to = p->to; - h->flags = 0; h->id = p->id; + assert(p->hop_limit <= HOP_MAX); + h->flags = p->hop_limit; // if the sender nodenum is zero, that means uninitialized assert(h->from); diff --git a/src/mesh/RadioInterface.h b/src/mesh/RadioInterface.h index 6c7dbd79..80661759 100644 --- a/src/mesh/RadioInterface.h +++ b/src/mesh/RadioInterface.h @@ -16,7 +16,14 @@ * wtih the old radiohead implementation. */ typedef struct { - uint8_t to, from, id, flags; + uint8_t to, from, id; + + /** + * Usage of flags: + * + * The bottom three bits of flags are use to store hop_limit when sent over the wire. + **/ + uint8_t flags; } PacketHeader; typedef enum { diff --git a/src/mesh/RadioLibInterface.cpp b/src/mesh/RadioLibInterface.cpp index e09c4f4a..78b9f661 100644 --- a/src/mesh/RadioLibInterface.cpp +++ b/src/mesh/RadioLibInterface.cpp @@ -288,13 +288,16 @@ void RadioLibInterface::handleReceiveInterrupt() rxGood++; if (h->to != 255 && h->to != ourAddr) { - DEBUG_MSG("ignoring packet not sent to us\n"); + DEBUG_MSG("FIXME - should snoop packet not sent to us\n"); } else { MeshPacket *mp = packetPool.allocZeroed(); mp->from = h->from; mp->to = h->to; mp->id = h->id; + assert(HOP_MAX <= 0x07); // If hopmax changes, carefully check this code + mp->hop_limit = h->flags & 0x07; + addReceiveMetadata(mp); mp->which_payload = MeshPacket_encrypted_tag; // Mark that the payload is still encrypted at this point diff --git a/src/mesh/Router.h b/src/mesh/Router.h index 20378371..77538a0b 100644 --- a/src/mesh/Router.h +++ b/src/mesh/Router.h @@ -59,7 +59,8 @@ class Router * Handle any packet that is received by an interface on this node. * Note: some packets may merely being passed through this node and will be forwarded elsewhere. * - * Note: this method will free the provided packet + * Note: this packet will never be called for messages sent/generated by this node. + * Note: this method will free the provided packet. */ virtual void handleReceived(MeshPacket *p); };