From fb3b62f8f0b85ea57e922770bdf410b8ba4a70c9 Mon Sep 17 00:00:00 2001 From: geeksville Date: Sat, 23 May 2020 15:48:23 -0700 Subject: [PATCH] CSR WIP --- platformio.ini | 2 +- proto | 2 +- src/mesh/DSRRouter.cpp | 49 ++++++++++++++++++++++++++++--------- src/mesh/DSRRouter.h | 17 ++++++++++++- src/mesh/ReliableRouter.cpp | 25 +++++++++++++------ src/mesh/ReliableRouter.h | 6 +++++ src/mesh/Router.cpp | 4 +++ src/mesh/mesh.pb.c | 1 + src/mesh/mesh.pb.h | 48 ++++++++++++++++++++++++------------ 9 files changed, 117 insertions(+), 37 deletions(-) diff --git a/platformio.ini b/platformio.ini index 28d8b63e8..761bf298d 100644 --- a/platformio.ini +++ b/platformio.ini @@ -9,7 +9,7 @@ ; https://docs.platformio.org/page/projectconf.html [platformio] -default_envs = nrf52dk ; Note: the github actions CI test build can't yet build NRF52 targets +default_envs = tbeam ; Note: the github actions CI test build can't yet build NRF52 targets [common] ; common is not currently used diff --git a/proto b/proto index e95239a2e..f713e0c03 160000 --- a/proto +++ b/proto @@ -1 +1 @@ -Subproject commit e95239a2ec9ac41ba77fac47ae531640bb0f8078 +Subproject commit f713e0c039140a1f2189274c9c28f219943e88de diff --git a/src/mesh/DSRRouter.cpp b/src/mesh/DSRRouter.cpp index 99ef1a656..d59fc36e2 100644 --- a/src/mesh/DSRRouter.cpp +++ b/src/mesh/DSRRouter.cpp @@ -42,24 +42,24 @@ void DSRRouter::sniffReceived(const MeshPacket *p) // FIXME, update nodedb // Handle route discovery packets (will be a broadcast message) - if (p->decoded.which_payload == SubPacket_request_tag) { + if (p->decoded.which_payload == SubPacket_route_request_tag) { // FIXME - always start request with the senders nodenum - if (weAreInRoute(p->decoded.request)) { + if (weAreInRoute(p->decoded.route_request)) { DEBUG_MSG("Ignoring a route request that contains us\n"); } else { - updateRoutes(p->decoded.request, + updateRoutes(p->decoded.route_request, false); // Update our routing tables based on the route that came in so far on this request if (p->decoded.dest == getNodeNum()) { // They were looking for us, send back a route reply (the sender address will be first in the list) - sendRouteReply(p->decoded.request); + sendRouteReply(p->decoded.route_request); } else { // They were looking for someone else, forward it along (as a zero hop broadcast) NodeNum nextHop = getNextHop(p->decoded.dest); if (nextHop) { // in our route cache, reply to the requester (the sender address will be first in the list) - sendRouteReply(p->decoded.request, nextHop); + sendRouteReply(p->decoded.route_request, nextHop); } else { // Not in our route cache, rebroadcast on their behalf (after adding ourselves to the request route) resendRouteRequest(p); @@ -69,28 +69,55 @@ void DSRRouter::sniffReceived(const MeshPacket *p) } // Handle route reply packets - if (p->decoded.which_payload == SubPacket_reply_tag) { - updateRoutes(p->decoded.reply, true); + if (p->decoded.which_payload == SubPacket_route_reply_tag) { + updateRoutes(p->decoded.route_reply, true); + } + + // Handle route error packets + if (p->decoded.which_payload == SubPacket_route_error_tag) { + // FIXME } // Learn 0 hop routes by just hearing any adjacent nodes // But treat broadcasts carefully, because when flood broadcasts go out they keep the same original "from". So we want to // ignore rebroadcasts. if (p->to != NODENUM_BROADCAST || p->hop_limit != HOP_RELIABLE) { - setRoute(p->from, p->from, 0); // We are adjacent with zero hops + addRoute(p->from, p->from, 0); // We are adjacent with zero hops } - // FIXME - handle any naks we receive (either because they are passing by us or someone naked a message we sent) + // We simply ignore ACKs - because ReliableRouter will delete the pending packet for us // Handle regular packets if (p->to == getNodeNum()) { // Destined for us (at least for this hop) // We need to route this packet to some other node if (p->decoded.dest && p->decoded.dest != p->to) { - // FIXME if we have a route out, resend the packet to the next hop, otherwise return a nak with no-route available + // FIXME if we have a route out, resend the packet to the next hop, otherwise return RouteError no-route available + + NodeNum nextHop = getNextHop(p->decoded.dest); + if (nextHop) { + sendNextHop(nextHop, p); // start a reliable single hop send + } else { + // We don't have a route out + assert(p->decoded.source); // I think this is guaranteed by now + + sendRouteError(p, RouteError_NO_ROUTE); + } + + // FIXME, stop local processing of this packet } - // FIXME - handle naks from our adjacent nodes - convert them to route error packets + // handle naks - convert them to route error packets + // All naks are generated locally, because we failed resending the packet too many times + PacketId nakId = p->decoded.which_ack == SubPacket_fail_id_tag ? p->decoded.ack.fail_id : 0; + if (nakId) { + auto pending = findPendingPacket(p->to, nakId); + if (pending && pending->packet->decoded.source) { // if source not set, this was not a multihop packet, just ignore + removeRoute(pending->packet->decoded.dest, p->to); // We no longer have a route to the specified node + + sendRouteError(p, RouteError_GOT_NAK); + } + } } return ReliableRouter::sniffReceived(p); diff --git a/src/mesh/DSRRouter.h b/src/mesh/DSRRouter.h index 62ec9c672..a6004260a 100644 --- a/src/mesh/DSRRouter.h +++ b/src/mesh/DSRRouter.h @@ -42,5 +42,20 @@ class DSRRouter : public ReliableRouter * If our routing tables already have something that can reach that node in fewer hops we will keep the existing route * instead. */ - void setRoute(NodeNum dest, NodeNum forwarder, uint8_t numHops); + void addRoute(NodeNum dest, NodeNum forwarder, uint8_t numHops); + + /** + * Record that the specified forwarder no longer has a route to the dest + */ + void removeRoute(NodeNum dest, NodeNum forwarder); + + /** + * Forward the specified packet to the specified node + */ + void sendNextHop(NodeNum n, const MeshPacket *p); + + /** + * Send a route error packet towards whoever originally sent this message + */ + void sendRouteError(const MeshPacket *p, RouteError err); }; \ No newline at end of file diff --git a/src/mesh/ReliableRouter.cpp b/src/mesh/ReliableRouter.cpp index c8e45a604..7056e36eb 100644 --- a/src/mesh/ReliableRouter.cpp +++ b/src/mesh/ReliableRouter.cpp @@ -50,7 +50,8 @@ void ReliableRouter::sniffReceived(const MeshPacket *p) DEBUG_MSG("Someone is retransmitting for us, generate implicit ack\n"); sendAckNak(true, p->from, p->id); } - } else if (p->to == ourNode) { // ignore ack/nak/want_ack packets that are not address to us (for now) + } else if (p->to == ourNode) { // ignore ack/nak/want_ack packets that are not address to us (we only handle 0 hop reliability + // - not DSR routing) if (p->want_ack) { sendAckNak(true, p->from, p->id); } @@ -60,8 +61,7 @@ void ReliableRouter::sniffReceived(const MeshPacket *p) PacketId ackId = p->decoded.which_ack == SubPacket_success_id_tag ? p->decoded.ack.success_id : 0; PacketId nakId = p->decoded.which_ack == SubPacket_fail_id_tag ? p->decoded.ack.fail_id : 0; - // we are careful to only read/update wasSeenRecently _after_ confirming this is an ack (to not mess - // up broadcasts) + // we are careful to only read wasSeenRecently - not update it (to not mess up broadcasts) if ((ackId || nakId) && !wasSeenRecently(p, false)) { if (ackId) { DEBUG_MSG("Received a ack=%d, stopping retransmissions\n", ackId); @@ -107,6 +107,14 @@ PendingPacket::PendingPacket(MeshPacket *p) setNextTx(); } +PendingPacket *ReliableRouter::findPendingPacket(GlobalPacketId key) +{ + auto old = pending.find(key); // If we have an old record, someone messed up because id got reused + if (old != pending.end()) { + return &old->second; + } else + return NULL; +} /** * Stop any retransmissions we are doing of the specified node/packet ID pair */ @@ -118,15 +126,16 @@ bool ReliableRouter::stopRetransmission(NodeNum from, PacketId id) bool ReliableRouter::stopRetransmission(GlobalPacketId key) { - auto old = pending.find(key); // If we have an old record, someone messed up because id got reused - if (old != pending.end()) { + auto old = findPendingPacket(key); + if (old) { auto numErased = pending.erase(key); assert(numErased == 1); - packetPool.release(old->second.packet); + packetPool.release(old->packet); return true; } else return false; } + /** * Add p to the list of packets to retransmit occasionally. We will free it once we stop retransmitting. */ @@ -158,7 +167,9 @@ void ReliableRouter::doRetransmissions() DEBUG_MSG("Reliable send failed, returning a nak fr=0x%x,to=0x%x,id=%d\n", p.packet->from, p.packet->to, p.packet->id); sendAckNak(false, p.packet->from, p.packet->id); - stopRetransmission(it->first); + // Note: we don't stop retransmission here, instead the Nak packet gets processed in sniffReceived - which + // allows the DSR version to still be able to look at the PendingPacket + // stopRetransmission(it->first); } else { DEBUG_MSG("Sending reliable retransmission fr=0x%x,to=0x%x,id=%d, tries left=%d\n", p.packet->from, p.packet->to, p.packet->id, p.numRetransmissions); diff --git a/src/mesh/ReliableRouter.h b/src/mesh/ReliableRouter.h index f8a238341..b96176864 100644 --- a/src/mesh/ReliableRouter.h +++ b/src/mesh/ReliableRouter.h @@ -92,6 +92,12 @@ class ReliableRouter : public FloodingRouter */ virtual void sniffReceived(const MeshPacket *p); + /** + * Try to find the pending packet record for this ID (or NULL if not found) + */ + PendingPacket *findPendingPacket(NodeNum from, PacketId id) { return findPendingPacket(GlobalPacketId(from, id)); } + PendingPacket *findPendingPacket(GlobalPacketId p); + private: /** * Send an ack or a nak packet back towards whoever sent idFrom diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index fba0aee4b..bf92f3f1e 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -96,6 +96,10 @@ ErrorCode Router::send(MeshPacket *p) { assert(p->to != nodeDB.getNodeNum()); // should have already been handled by sendLocal + PacketId nakId = p->decoded.which_ack == SubPacket_fail_id_tag ? p->decoded.ack.fail_id : 0; + assert( + !nakId); // I don't think we ever send 0hop naks over the wire (other than to the phone), test that assumption with assert + // Never set the want_ack flag on broadcast packets sent over the air. if (p->to == NODENUM_BROADCAST) p->want_ack = false; diff --git a/src/mesh/mesh.pb.c b/src/mesh/mesh.pb.c index 321576556..4eb53e1ba 100644 --- a/src/mesh/mesh.pb.c +++ b/src/mesh/mesh.pb.c @@ -58,3 +58,4 @@ PB_BIND(ManufacturingData, ManufacturingData, AUTO) + diff --git a/src/mesh/mesh.pb.h b/src/mesh/mesh.pb.h index 3a3241613..087af997e 100644 --- a/src/mesh/mesh.pb.h +++ b/src/mesh/mesh.pb.h @@ -14,6 +14,12 @@ extern "C" { #endif /* Enum definitions */ +typedef enum _RouteError { + RouteError_NONE = 0, + RouteError_NO_ROUTE = 1, + RouteError_GOT_NAK = 2 +} RouteError; + typedef enum _Constants { Constants_Unused = 0 } Constants; @@ -130,8 +136,9 @@ typedef struct _SubPacket { Position position; Data data; User user; - RouteDiscovery request; - RouteDiscovery reply; + RouteDiscovery route_request; + RouteDiscovery route_reply; + RouteError route_error; }; bool want_response; uint32_t dest; @@ -140,6 +147,7 @@ typedef struct _SubPacket { uint32_t success_id; uint32_t fail_id; } ack; + uint32_t source; } SubPacket; typedef PB_BYTES_ARRAY_T(256) MeshPacket_encrypted_t; @@ -200,6 +208,10 @@ typedef struct _ToRadio { /* Helper constants for enums */ +#define _RouteError_MIN RouteError_NONE +#define _RouteError_MAX RouteError_GOT_NAK +#define _RouteError_ARRAYSIZE ((RouteError)(RouteError_GOT_NAK+1)) + #define _Constants_MIN Constants_Unused #define _Constants_MAX Constants_Unused #define _Constants_ARRAYSIZE ((Constants)(Constants_Unused+1)) @@ -218,7 +230,7 @@ typedef struct _ToRadio { #define Data_init_default {_Data_Type_MIN, {0, {0}}} #define User_init_default {"", "", "", {0}} #define RouteDiscovery_init_default {0, {0, 0, 0, 0, 0, 0, 0, 0}} -#define SubPacket_init_default {0, {Position_init_default}, 0, 0, 0, {0}} +#define SubPacket_init_default {0, {Position_init_default}, 0, 0, 0, {0}, 0} #define MeshPacket_init_default {0, 0, 0, {SubPacket_init_default}, 0, 0, 0, 0, 0} #define ChannelSettings_init_default {0, _ChannelSettings_ModemConfig_MIN, {0, {0}}, ""} #define RadioConfig_init_default {false, RadioConfig_UserPreferences_init_default, false, ChannelSettings_init_default} @@ -234,7 +246,7 @@ typedef struct _ToRadio { #define Data_init_zero {_Data_Type_MIN, {0, {0}}} #define User_init_zero {"", "", "", {0}} #define RouteDiscovery_init_zero {0, {0, 0, 0, 0, 0, 0, 0, 0}} -#define SubPacket_init_zero {0, {Position_init_zero}, 0, 0, 0, {0}} +#define SubPacket_init_zero {0, {Position_init_zero}, 0, 0, 0, {0}, 0} #define MeshPacket_init_zero {0, 0, 0, {SubPacket_init_zero}, 0, 0, 0, 0, 0} #define ChannelSettings_init_zero {0, _ChannelSettings_ModemConfig_MIN, {0, {0}}, ""} #define RadioConfig_init_zero {false, RadioConfig_UserPreferences_init_zero, false, ChannelSettings_init_zero} @@ -302,12 +314,14 @@ typedef struct _ToRadio { #define SubPacket_position_tag 1 #define SubPacket_data_tag 3 #define SubPacket_user_tag 4 -#define SubPacket_request_tag 6 -#define SubPacket_reply_tag 7 +#define SubPacket_route_request_tag 6 +#define SubPacket_route_reply_tag 7 +#define SubPacket_route_error_tag 13 #define SubPacket_success_id_tag 10 #define SubPacket_fail_id_tag 11 #define SubPacket_want_response_tag 5 #define SubPacket_dest_tag 9 +#define SubPacket_source_tag 12 #define MeshPacket_decoded_tag 3 #define MeshPacket_encrypted_tag 8 #define MeshPacket_from_tag 1 @@ -370,19 +384,21 @@ X(a, STATIC, REPEATED, INT32, route, 2) X(a, STATIC, ONEOF, MESSAGE, (payload,position,position), 1) \ X(a, STATIC, ONEOF, MESSAGE, (payload,data,data), 3) \ X(a, STATIC, ONEOF, MESSAGE, (payload,user,user), 4) \ -X(a, STATIC, ONEOF, MESSAGE, (payload,request,request), 6) \ -X(a, STATIC, ONEOF, MESSAGE, (payload,reply,reply), 7) \ +X(a, STATIC, ONEOF, MESSAGE, (payload,route_request,route_request), 6) \ +X(a, STATIC, ONEOF, MESSAGE, (payload,route_reply,route_reply), 7) \ +X(a, STATIC, ONEOF, ENUM, (payload,route_error,route_error), 13) \ X(a, STATIC, SINGULAR, BOOL, want_response, 5) \ X(a, STATIC, SINGULAR, UINT32, dest, 9) \ X(a, STATIC, ONEOF, UINT32, (ack,success_id,ack.success_id), 10) \ -X(a, STATIC, ONEOF, UINT32, (ack,fail_id,ack.fail_id), 11) +X(a, STATIC, ONEOF, UINT32, (ack,fail_id,ack.fail_id), 11) \ +X(a, STATIC, SINGULAR, UINT32, source, 12) #define SubPacket_CALLBACK NULL #define SubPacket_DEFAULT NULL #define SubPacket_payload_position_MSGTYPE Position #define SubPacket_payload_data_MSGTYPE Data #define SubPacket_payload_user_MSGTYPE User -#define SubPacket_payload_request_MSGTYPE RouteDiscovery -#define SubPacket_payload_reply_MSGTYPE RouteDiscovery +#define SubPacket_payload_route_request_MSGTYPE RouteDiscovery +#define SubPacket_payload_route_reply_MSGTYPE RouteDiscovery #define MeshPacket_FIELDLIST(X, a) \ X(a, STATIC, SINGULAR, UINT32, from, 1) \ @@ -554,17 +570,17 @@ extern const pb_msgdesc_t ManufacturingData_msg; #define Data_size 256 #define User_size 72 #define RouteDiscovery_size 88 -#define SubPacket_size 273 -#define MeshPacket_size 312 +#define SubPacket_size 279 +#define MeshPacket_size 318 #define ChannelSettings_size 60 #define RadioConfig_size 157 #define RadioConfig_UserPreferences_size 93 #define NodeInfo_size 132 #define MyNodeInfo_size 80 -#define DeviceState_size 15037 +#define DeviceState_size 15235 #define DebugString_size 258 -#define FromRadio_size 321 -#define ToRadio_size 315 +#define FromRadio_size 327 +#define ToRadio_size 321 /* ManufacturingData_size depends on runtime parameters */ #ifdef __cplusplus