sforkowany z mirror/meshtastic-firmware
wip reliable unicast (1 hop)
rodzic
cca4867987
commit
8bf4919576
|
@ -33,7 +33,7 @@
|
|||
#include "error.h"
|
||||
#include "power.h"
|
||||
// #include "rom/rtc.h"
|
||||
#include "FloodingRouter.h"
|
||||
#include "ReliableRouter.h"
|
||||
#include "main.h"
|
||||
#include "screen.h"
|
||||
#include "sleep.h"
|
||||
|
@ -53,7 +53,7 @@ meshtastic::PowerStatus powerStatus;
|
|||
bool ssd1306_found;
|
||||
bool axp192_found;
|
||||
|
||||
FloodingRouter realRouter;
|
||||
ReliableRouter realRouter;
|
||||
Router &router = realRouter; // Users of router don't care what sort of subclass implements that API
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
|
|
@ -27,10 +27,9 @@
|
|||
Any entries in recentBroadcasts that are older than X seconds (longer than the
|
||||
max time a flood can take) will be discarded.
|
||||
*/
|
||||
class FloodingRouter : public Router, private PacketHistory
|
||||
class FloodingRouter : public Router, protected PacketHistory
|
||||
{
|
||||
private:
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
|
|
|
@ -46,8 +46,6 @@ MeshService service;
|
|||
|
||||
#include "Router.h"
|
||||
|
||||
#define NUM_PACKET_ID 255 // 0 is consider invalid
|
||||
|
||||
static uint32_t sendOwnerCb()
|
||||
{
|
||||
service.sendOurOwner();
|
||||
|
@ -57,23 +55,6 @@ static uint32_t sendOwnerCb()
|
|||
|
||||
static Periodic sendOwnerPeriod(sendOwnerCb);
|
||||
|
||||
/// Generate a unique packet id
|
||||
// FIXME, move this someplace better
|
||||
PacketId generatePacketId()
|
||||
{
|
||||
static uint32_t i; // Note: trying to keep this in noinit didn't help for working across reboots
|
||||
static bool didInit = false;
|
||||
|
||||
if (!didInit) {
|
||||
didInit = true;
|
||||
i = random(0, NUM_PACKET_ID +
|
||||
1); // pick a random initial sequence number at boot (to prevent repeated reboots always starting at 0)
|
||||
}
|
||||
|
||||
i++;
|
||||
return (i % NUM_PACKET_ID) + 1; // return number between 1 and 255
|
||||
}
|
||||
|
||||
MeshService::MeshService() : toPhoneQueue(MAX_RX_TOPHONE)
|
||||
{
|
||||
// assert(MAX_RX_TOPHONE == 32); // FIXME, delete this, just checking my clever macro
|
||||
|
@ -90,7 +71,7 @@ void MeshService::init()
|
|||
|
||||
void MeshService::sendOurOwner(NodeNum dest, bool wantReplies)
|
||||
{
|
||||
MeshPacket *p = allocForSending();
|
||||
MeshPacket *p = router.allocForSending();
|
||||
p->to = dest;
|
||||
p->decoded.want_response = wantReplies;
|
||||
p->decoded.which_payload = SubPacket_user_tag;
|
||||
|
@ -265,33 +246,13 @@ void MeshService::sendToMesh(MeshPacket *p)
|
|||
DEBUG_MSG("Providing time to mesh %u\n", p->decoded.position.time);
|
||||
}
|
||||
|
||||
// If the phone sent a packet just to us, don't send it out into the network
|
||||
if (p->to == nodeDB.getNodeNum()) {
|
||||
DEBUG_MSG("Dropping locally processed message\n");
|
||||
// Note: We might return !OK if our fifo was full, at that point the only option we have is to drop it
|
||||
if (router.send(p) != ERRNO_OK) {
|
||||
DEBUG_MSG("No radio was able to send packet, discarding...\n");
|
||||
releaseToPool(p);
|
||||
} else {
|
||||
// Note: We might return !OK if our fifo was full, at that point the only option we have is to drop it
|
||||
if (router.send(p) != ERRNO_OK) {
|
||||
DEBUG_MSG("No radio was able to send packet, discarding...\n");
|
||||
releaseToPool(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MeshPacket *MeshService::allocForSending()
|
||||
{
|
||||
MeshPacket *p = packetPool.allocZeroed();
|
||||
|
||||
p->which_payload = MeshPacket_decoded_tag; // Assume payload is decoded at start.
|
||||
p->from = nodeDB.getNodeNum();
|
||||
p->to = NODENUM_BROADCAST;
|
||||
p->hop_limit = HOP_RELIABLE;
|
||||
p->id = generatePacketId();
|
||||
p->rx_time = getValidTime(); // Just in case we process the packet locally - make sure it has a valid timestamp
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
void MeshService::sendNetworkPing(NodeNum dest, bool wantReplies)
|
||||
{
|
||||
NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum());
|
||||
|
@ -311,7 +272,7 @@ void MeshService::sendOurPosition(NodeNum dest, bool wantReplies)
|
|||
assert(node->has_position);
|
||||
|
||||
// Update our local node info with our position (even if we don't decide to update anyone else)
|
||||
MeshPacket *p = allocForSending();
|
||||
MeshPacket *p = router.allocForSending();
|
||||
p->to = dest;
|
||||
p->decoded.which_payload = SubPacket_position_tag;
|
||||
p->decoded.position = node->position;
|
||||
|
@ -325,7 +286,7 @@ int MeshService::onGPSChanged(void *unused)
|
|||
// DEBUG_MSG("got gps notify\n");
|
||||
|
||||
// Update our local node info with our position (even if we don't decide to update anyone else)
|
||||
MeshPacket *p = allocForSending();
|
||||
MeshPacket *p = router.allocForSending();
|
||||
p->decoded.which_payload = SubPacket_position_tag;
|
||||
|
||||
Position &pos = p->decoded.position;
|
||||
|
|
|
@ -67,9 +67,6 @@ class MeshService
|
|||
/// The owner User record just got updated, update our node DB and broadcast the info into the mesh
|
||||
void reloadOwner() { sendOurOwner(); }
|
||||
|
||||
/// Allocate and return a meshpacket which defaults as send to broadcast from the current node.
|
||||
MeshPacket *allocForSending();
|
||||
|
||||
/// Called when the user wakes up our GUI, normally sends our latest location to the mesh (if we have it), otherwise at least
|
||||
/// sends our owner
|
||||
void sendNetworkPing(NodeNum dest, bool wantReplies = false);
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
#include "ReliableRouter.h"
|
||||
#include "MeshTypes.h"
|
||||
#include "configuration.h"
|
||||
#include "mesh-pb-constants.h"
|
||||
|
||||
// ReliableRouter::ReliableRouter() {}
|
||||
|
||||
/**
|
||||
* If the message is want_ack, then add it to a list of packets to retransmit.
|
||||
* If we run out of retransmissions, send a nak packet towards the original client to indicate failure.
|
||||
*/
|
||||
ErrorCode ReliableRouter::send(MeshPacket *p)
|
||||
{
|
||||
if (p->want_ack) {
|
||||
auto copy = packetPool.allocCopy(*p);
|
||||
startRetransmission(copy);
|
||||
}
|
||||
|
||||
return FloodingRouter::send(p);
|
||||
}
|
||||
|
||||
/**
|
||||
* If we receive a want_ack packet (do not check for wasSeenRecently), send back an ack (this might generate multiple ack sends in
|
||||
* case the our first ack gets lost)
|
||||
*
|
||||
* If we receive an ack packet (do check wasSeenRecently), clear out any retransmissions and
|
||||
* forward the ack to the application layer.
|
||||
*
|
||||
* If we receive a nak packet (do check wasSeenRecently), clear out any retransmissions
|
||||
* and forward the nak to the application layer.
|
||||
*
|
||||
* Otherwise, let superclass handle it.
|
||||
*/
|
||||
void ReliableRouter::handleReceived(MeshPacket *p)
|
||||
{
|
||||
if (p->to == getNodeNum()) { // ignore ack/nak/want_ack packets that are not address to us (for now)
|
||||
if (p->want_ack) {
|
||||
sendAckNak(true, p->from, p->id);
|
||||
}
|
||||
|
||||
if (perhapsDecode(p)) {
|
||||
// If the payload is valid, look for ack/nak
|
||||
|
||||
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)
|
||||
if ((ackId || nakId) && !wasSeenRecently(p)) {
|
||||
if (ackId) {
|
||||
DEBUG_MSG("Received a ack=%d, stopping retransmissions\n", ackId);
|
||||
stopRetransmission(p->to, ackId);
|
||||
} else {
|
||||
DEBUG_MSG("Received a nak=%d, stopping retransmissions\n", nakId);
|
||||
stopRetransmission(p->to, nakId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// handle the packet as normal
|
||||
FloodingRouter::handleReceived(p);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an ack or a nak packet back towards whoever sent idFrom
|
||||
*/
|
||||
void ReliableRouter::sendAckNak(bool isAck, NodeNum to, PacketId idFrom)
|
||||
{
|
||||
DEBUG_MSG("Sending an ack=%d,to=%d,idFrom=%d", isAck, to, idFrom);
|
||||
auto p = allocForSending();
|
||||
p->hop_limit = 0; // Assume just immediate neighbors for now
|
||||
p->to = to;
|
||||
|
||||
if (isAck) {
|
||||
p->decoded.ack.success_id = idFrom;
|
||||
p->decoded.which_ack = SubPacket_success_id_tag;
|
||||
} else {
|
||||
p->decoded.ack.fail_id = idFrom;
|
||||
p->decoded.which_ack = SubPacket_fail_id_tag;
|
||||
}
|
||||
|
||||
send(p);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop any retransmissions we are doing of the specified node/packet ID pair
|
||||
*/
|
||||
void ReliableRouter::stopRetransmission(NodeNum from, PacketId id) {}
|
||||
|
||||
/**
|
||||
* Add p to the list of packets to retransmit occasionally. We will free it once we stop retransmitting.
|
||||
*/
|
||||
void ReliableRouter::startRetransmission(MeshPacket *p) {}
|
||||
|
||||
/**
|
||||
* Do any retransmissions that are scheduled (FIXME - for the time being called from loop)
|
||||
*/
|
||||
void ReliableRouter::doRetransmissions() {}
|
|
@ -0,0 +1,63 @@
|
|||
#pragma once
|
||||
|
||||
#include "FloodingRouter.h"
|
||||
#include "PeriodicTask.h"
|
||||
|
||||
/**
|
||||
* This is a mixin that extends Router with the ability to do (one hop only) reliable message sends.
|
||||
*/
|
||||
class ReliableRouter : public FloodingRouter
|
||||
{
|
||||
private:
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
*/
|
||||
// ReliableRouter();
|
||||
|
||||
/**
|
||||
* Send a packet on a suitable interface. This routine will
|
||||
* later free() the packet to pool. This routine is not allowed to stall.
|
||||
* If the txmit queue is full it might return an error
|
||||
*/
|
||||
virtual ErrorCode send(MeshPacket *p);
|
||||
|
||||
/** Do our retransmission handling */
|
||||
virtual void loop()
|
||||
{
|
||||
doRetransmissions();
|
||||
FloodingRouter::loop();
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Called from loop()
|
||||
* 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
|
||||
*/
|
||||
virtual void handleReceived(MeshPacket *p);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Send an ack or a nak packet back towards whoever sent idFrom
|
||||
*/
|
||||
void sendAckNak(bool isAck, NodeNum to, PacketId idFrom);
|
||||
|
||||
/**
|
||||
* Stop any retransmissions we are doing of the specified node/packet ID pair
|
||||
*/
|
||||
void stopRetransmission(NodeNum from, PacketId id);
|
||||
|
||||
/**
|
||||
* Add p to the list of packets to retransmit occasionally. We will free it once we stop retransmitting.
|
||||
*/
|
||||
void startRetransmission(MeshPacket *p);
|
||||
|
||||
/**
|
||||
* Do any retransmissions that are scheduled (FIXME - for the time being called from loop)
|
||||
*/
|
||||
void doRetransmissions();
|
||||
};
|
|
@ -44,6 +44,39 @@ void Router::loop()
|
|||
}
|
||||
}
|
||||
|
||||
#define NUM_PACKET_ID 255 // 0 is consider invalid
|
||||
|
||||
/// Generate a unique packet id
|
||||
// FIXME, move this someplace better
|
||||
PacketId generatePacketId()
|
||||
{
|
||||
static uint32_t i; // Note: trying to keep this in noinit didn't help for working across reboots
|
||||
static bool didInit = false;
|
||||
|
||||
if (!didInit) {
|
||||
didInit = true;
|
||||
i = random(0, NUM_PACKET_ID +
|
||||
1); // pick a random initial sequence number at boot (to prevent repeated reboots always starting at 0)
|
||||
}
|
||||
|
||||
i++;
|
||||
return (i % NUM_PACKET_ID) + 1; // return number between 1 and 255
|
||||
}
|
||||
|
||||
MeshPacket *Router::allocForSending()
|
||||
{
|
||||
MeshPacket *p = packetPool.allocZeroed();
|
||||
|
||||
p->which_payload = MeshPacket_decoded_tag; // Assume payload is decoded at start.
|
||||
p->from = nodeDB.getNodeNum();
|
||||
p->to = NODENUM_BROADCAST;
|
||||
p->hop_limit = HOP_RELIABLE;
|
||||
p->id = generatePacketId();
|
||||
p->rx_time = getValidTime(); // Just in case we process the packet locally - make sure it has a valid timestamp
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a packet on a suitable interface. This routine will
|
||||
* later free() the packet to pool. This routine is not allowed to stall.
|
||||
|
@ -51,33 +84,40 @@ void Router::loop()
|
|||
*/
|
||||
ErrorCode Router::send(MeshPacket *p)
|
||||
{
|
||||
// If the packet hasn't yet been encrypted, do so now (it might already be encrypted if we are just forwarding it)
|
||||
|
||||
assert(p->which_payload == MeshPacket_encrypted_tag ||
|
||||
p->which_payload == MeshPacket_decoded_tag); // I _think_ all packets should have a payload by now
|
||||
|
||||
// First convert from protobufs to raw bytes
|
||||
if (p->which_payload == MeshPacket_decoded_tag) {
|
||||
static uint8_t bytes[MAX_RHPACKETLEN]; // we have to use a scratch buffer because a union
|
||||
|
||||
size_t numbytes = pb_encode_to_bytes(bytes, sizeof(bytes), SubPacket_fields, &p->decoded);
|
||||
|
||||
assert(numbytes <= MAX_RHPACKETLEN);
|
||||
crypto->encrypt(p->from, p->id, numbytes, bytes);
|
||||
|
||||
// Copy back into the packet and set the variant type
|
||||
memcpy(p->encrypted.bytes, bytes, numbytes);
|
||||
p->encrypted.size = numbytes;
|
||||
p->which_payload = MeshPacket_encrypted_tag;
|
||||
}
|
||||
|
||||
if (iface) {
|
||||
// DEBUG_MSG("Sending packet via interface fr=0x%x,to=0x%x,id=%d\n", p->from, p->to, p->id);
|
||||
return iface->send(p);
|
||||
} else {
|
||||
DEBUG_MSG("Dropping packet - no interfaces - fr=0x%x,to=0x%x,id=%d\n", p->from, p->to, p->id);
|
||||
// If this packet was destined only to apps on our node, don't send it out into the network
|
||||
if (p->to == nodeDB.getNodeNum()) {
|
||||
DEBUG_MSG("Dropping locally processed message\n");
|
||||
packetPool.release(p);
|
||||
return ERRNO_NO_INTERFACES;
|
||||
return ERRNO_OK;
|
||||
} else {
|
||||
// If the packet hasn't yet been encrypted, do so now (it might already be encrypted if we are just forwarding it)
|
||||
|
||||
assert(p->which_payload == MeshPacket_encrypted_tag ||
|
||||
p->which_payload == MeshPacket_decoded_tag); // I _think_ all packets should have a payload by now
|
||||
|
||||
// First convert from protobufs to raw bytes
|
||||
if (p->which_payload == MeshPacket_decoded_tag) {
|
||||
static uint8_t bytes[MAX_RHPACKETLEN]; // we have to use a scratch buffer because a union
|
||||
|
||||
size_t numbytes = pb_encode_to_bytes(bytes, sizeof(bytes), SubPacket_fields, &p->decoded);
|
||||
|
||||
assert(numbytes <= MAX_RHPACKETLEN);
|
||||
crypto->encrypt(p->from, p->id, numbytes, bytes);
|
||||
|
||||
// Copy back into the packet and set the variant type
|
||||
memcpy(p->encrypted.bytes, bytes, numbytes);
|
||||
p->encrypted.size = numbytes;
|
||||
p->which_payload = MeshPacket_encrypted_tag;
|
||||
}
|
||||
|
||||
if (iface) {
|
||||
// DEBUG_MSG("Sending packet via interface fr=0x%x,to=0x%x,id=%d\n", p->from, p->to, p->id);
|
||||
return iface->send(p);
|
||||
} else {
|
||||
DEBUG_MSG("Dropping packet - no interfaces - fr=0x%x,to=0x%x,id=%d\n", p->from, p->to, p->id);
|
||||
packetPool.release(p);
|
||||
return ERRNO_NO_INTERFACES;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -90,18 +130,12 @@ void Router::sniffReceived(MeshPacket *p)
|
|||
DEBUG_MSG("Sniffing packet not sent to us fr=0x%x,to=0x%x,id=%d\n", p->from, p->to, p->id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
void Router::handleReceived(MeshPacket *p)
|
||||
bool Router::perhapsDecode(MeshPacket *p)
|
||||
{
|
||||
// FIXME, this class shouldn't EVER need to know about the GPS, move getValidTime() into a non gps dependent function
|
||||
// Also, we should set the time from the ISR and it should have msec level resolution
|
||||
p->rx_time = getValidTime(); // store the arrival timestamp for the phone
|
||||
if (p->which_payload == MeshPacket_decoded_tag)
|
||||
return true; // If packet was already decoded just return
|
||||
|
||||
assert(p->which_payload ==
|
||||
MeshPacket_encrypted_tag); // I _think_ the only thing that pushes to us is raw devices that just received packets
|
||||
assert(p->which_payload == MeshPacket_encrypted_tag);
|
||||
|
||||
// FIXME - someday don't send routing packets encrypted. That would allow us to route for other channels without
|
||||
// being able to decrypt their data.
|
||||
|
@ -113,14 +147,37 @@ void Router::handleReceived(MeshPacket *p)
|
|||
|
||||
// Take those raw bytes and convert them back into a well structured protobuf we can understand
|
||||
if (!pb_decode_from_bytes(bytes, p->encrypted.size, SubPacket_fields, &p->decoded)) {
|
||||
DEBUG_MSG("Invalid protobufs in received mesh packet, discarding.\n");
|
||||
DEBUG_MSG("Invalid protobufs in received mesh packet!\n");
|
||||
return false;
|
||||
} else {
|
||||
// parsing was successful, queue for our recipient
|
||||
// parsing was successful
|
||||
p->which_payload = MeshPacket_decoded_tag;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
NodeNum Router::getNodeNum()
|
||||
{
|
||||
return nodeDB.getNodeNum();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
void Router::handleReceived(MeshPacket *p)
|
||||
{
|
||||
// FIXME, this class shouldn't EVER need to know about the GPS, move getValidTime() into a non gps dependent function
|
||||
// Also, we should set the time from the ISR and it should have msec level resolution
|
||||
p->rx_time = getValidTime(); // store the arrival timestamp for the phone
|
||||
|
||||
// Take those raw bytes and convert them back into a well structured protobuf we can understand
|
||||
if (perhapsDecode(p)) {
|
||||
// parsing was successful, queue for our recipient
|
||||
|
||||
sniffReceived(p);
|
||||
uint8_t ourAddr = nodeDB.getNodeNum();
|
||||
if (p->to == NODENUM_BROADCAST || p->to == ourAddr) {
|
||||
|
||||
if (p->to == NODENUM_BROADCAST || p->to == getNodeNum()) {
|
||||
DEBUG_MSG("Notifying observers of received packet fr=0x%x,to=0x%x,id=%d\n", p->from, p->to, p->id);
|
||||
notifyPacketReceived.notifyObservers(p);
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ class Router
|
|||
* do idle processing
|
||||
* Mostly looking in our incoming rxPacket queue and calling handleReceived.
|
||||
*/
|
||||
void loop();
|
||||
virtual void loop();
|
||||
|
||||
/**
|
||||
* Send a packet on a suitable interface. This routine will
|
||||
|
@ -53,6 +53,13 @@ class Router
|
|||
*/
|
||||
virtual ErrorCode send(MeshPacket *p);
|
||||
|
||||
/// Allocate and return a meshpacket which defaults as send to broadcast from the current node.
|
||||
MeshPacket *allocForSending();
|
||||
|
||||
/**
|
||||
* @return our local nodenum */
|
||||
NodeNum getNodeNum();
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Called from loop()
|
||||
|
@ -65,10 +72,21 @@ class Router
|
|||
virtual void handleReceived(MeshPacket *p);
|
||||
|
||||
/**
|
||||
* Every (non duplicate) packet this node receives will be passed through this method. This allows subclasses to
|
||||
* Every (non duplicate) packet this node receives will be passed through this method. This allows subclasses to
|
||||
* update routing tables etc... based on what we overhear (even for messages not destined to our node)
|
||||
*/
|
||||
virtual void sniffReceived(MeshPacket *p);
|
||||
|
||||
/**
|
||||
* Remove any encryption and decode the protobufs inside this packet (if necessary).
|
||||
*
|
||||
* @return true for success, false for corrupt packet.
|
||||
*/
|
||||
bool perhapsDecode(MeshPacket *p);
|
||||
};
|
||||
|
||||
extern Router &router;
|
||||
extern Router &router;
|
||||
|
||||
/// Generate a unique packet id
|
||||
// FIXME, move this someplace better
|
||||
PacketId generatePacketId();
|
Ładowanie…
Reference in New Issue