firmware/src/mesh/MeshModule.cpp

280 lines
11 KiB
C++
Raw Normal View History

#include "configuration.h"
#include "MeshModule.h"
#include "Channels.h"
2020-12-07 02:18:11 +00:00
#include "MeshService.h"
2021-03-05 03:44:45 +00:00
#include "NodeDB.h"
#include "modules/RoutingModule.h"
#include <assert.h>
std::vector<MeshModule *> *MeshModule::modules;
const MeshPacket *MeshModule::currentRequest;
2020-12-13 04:57:37 +00:00
2021-03-05 03:44:45 +00:00
/**
* If any of the current chain of modules has already sent a reply, it will be here. This is useful to allow
2021-03-05 03:44:45 +00:00
* the RoutingPlugin to avoid sending redundant acks
*/
MeshPacket *MeshModule::currentReply;
2021-03-05 03:44:45 +00:00
MeshModule::MeshModule(const char *_name) : name(_name)
{
// Can't trust static initalizer order, so we check each time
2022-02-27 09:49:24 +00:00
if (!modules)
modules = new std::vector<MeshModule *>();
2022-02-27 09:49:24 +00:00
modules->push_back(this);
}
void MeshModule::setup() {}
MeshModule::~MeshModule()
{
assert(0); // FIXME - remove from list of modules once someone needs this feature
}
MeshPacket *MeshModule::allocAckNak(Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex)
{
Routing c = Routing_init_default;
c.error_reason = err;
c.which_variant = Routing_error_reason_tag;
// Now that we have moded sendAckNak up one level into the class heirarchy we can no longer assume we are a RoutingPlugin
// So we manually call pb_encode_to_bytes and specify routing port number
// auto p = allocDataProtobuf(c);
MeshPacket *p = router->allocForSending();
p->decoded.portnum = PortNum_ROUTING_APP;
p->decoded.payload.size = pb_encode_to_bytes(p->decoded.payload.bytes, sizeof(p->decoded.payload.bytes), &Routing_msg, &c);
p->priority = MeshPacket_Priority_ACK;
p->hop_limit = config.lora.hop_limit; // Flood ACK back to original sender
p->to = to;
p->decoded.request_id = idFrom;
p->channel = chIndex;
2022-12-30 02:41:37 +00:00
LOG_DEBUG("Alloc an err=%d,to=0x%x,idFrom=0x%x,id=0x%x\n", err, to, idFrom, p->id);
return p;
}
MeshPacket *MeshModule::allocErrorResponse(Routing_Error err, const MeshPacket *p)
{
auto r = allocAckNak(err, getFrom(p), p->id, p->channel);
setReplyTo(r, *p);
return r;
}
void MeshModule::callPlugins(const MeshPacket &mp, RxSource src)
{
2022-12-30 02:41:37 +00:00
// LOG_DEBUG("In call modules\n");
2022-02-27 10:21:02 +00:00
bool moduleFound = false;
2021-02-17 11:04:41 +00:00
// We now allow **encrypted** packets to pass through the modules
bool isDecoded = mp.which_payload_variant == MeshPacket_decoded_tag;
2021-02-17 11:04:41 +00:00
2021-03-05 03:44:45 +00:00
currentReply = NULL; // No reply yet
2021-02-17 11:04:41 +00:00
// Was this message directed to us specifically? Will be false if we are sniffing someone elses packets
2021-02-23 03:43:30 +00:00
auto ourNodeNum = nodeDB.getNodeNum();
bool toUs = mp.to == NODENUM_BROADCAST || mp.to == ourNodeNum;
2022-02-27 09:49:24 +00:00
for (auto i = modules->begin(); i != modules->end(); ++i) {
auto &pi = **i;
2020-12-13 04:57:37 +00:00
pi.currentRequest = &mp;
2021-02-17 11:04:41 +00:00
/// We only call modules that are interested in the packet (and the message is destined to us or we are promiscious)
bool wantsPacket = (isDecoded || pi.encryptedOk) && (pi.isPromiscuous || toUs) && pi.wantPacket(&mp);
2021-03-23 04:07:04 +00:00
if ((src == RX_SRC_LOCAL) && !(pi.loopbackOk)) {
// new case, monitor separately for now, then FIXME merge above
wantsPacket = false;
}
assert(!pi.myReply); // If it is !null it means we have a bug, because it should have been sent the previous time
2021-03-23 04:07:04 +00:00
if (wantsPacket) {
2022-12-30 02:41:37 +00:00
LOG_DEBUG("Module '%s' wantsPacket=%d\n", pi.name, wantsPacket);
2022-02-27 10:21:02 +00:00
moduleFound = true;
2020-12-13 07:59:26 +00:00
/// received channel (or NULL if not decoded)
Channel *ch = isDecoded ? &channels.getByIndex(mp.channel) : NULL;
2021-03-23 04:07:04 +00:00
/// Is the channel this packet arrived on acceptable? (security check)
/// Note: we can't know channel names for encrypted packets, so those are NEVER sent to boundChannel modules
/// Also: if a packet comes in on the local PC interface, we don't check for bound channels, because it is TRUSTED and it needs to
/// to be able to fetch the initial admin packets without yet knowing any channels.
bool rxChannelOk = !pi.boundChannel || (mp.from == 0) || (strcasecmp(ch->settings.name, pi.boundChannel) == 0);
2021-03-23 04:07:04 +00:00
if (!rxChannelOk) {
// no one should have already replied!
assert(!currentReply);
2021-02-26 12:10:41 +00:00
if (mp.decoded.want_response) {
printPacket("packet on wrong channel, returning error", &mp);
currentReply = pi.allocErrorResponse(Routing_Error_NOT_AUTHORIZED, &mp);
2021-03-23 04:07:04 +00:00
} else
printPacket("packet on wrong channel, but can't respond", &mp);
2021-03-05 03:44:45 +00:00
} else {
2021-03-23 04:07:04 +00:00
ProcessMessage handled = pi.handleReceived(mp);
2021-03-23 04:07:04 +00:00
// Possibly send replies (but only if the message was directed to us specifically, i.e. not for promiscious
// sniffing) also: we only let the one module send a reply, once that happens, remaining modules are not
2021-03-23 04:07:04 +00:00
// considered
// NOTE: we send a reply *even if the (non broadcast) request was from us* which is unfortunate but necessary
// because currently when the phone sends things, it sends things using the local node ID as the from address. A
// better solution (FIXME) would be to let phones have their own distinct addresses and we 'route' to them like
// any other node.
if (mp.decoded.want_response && toUs && (getFrom(&mp) != ourNodeNum || mp.to == ourNodeNum) && !currentReply) {
pi.sendResponse(mp);
2022-12-30 02:41:37 +00:00
LOG_DEBUG("Module '%s' sent a response\n", pi.name);
2021-03-23 04:07:04 +00:00
} else {
2022-12-30 02:41:37 +00:00
LOG_DEBUG("Module '%s' considered\n", pi.name);
2021-03-23 04:07:04 +00:00
}
// If the requester didn't ask for a response we might need to discard unused replies to prevent memory leaks
if (pi.myReply) {
2022-12-30 02:41:37 +00:00
LOG_DEBUG("Discarding an unneeded response\n");
packetPool.release(pi.myReply);
pi.myReply = NULL;
}
if (handled == ProcessMessage::STOP) {
2022-12-30 02:41:37 +00:00
LOG_DEBUG("Module '%s' handled and skipped other processing\n", pi.name);
2021-03-23 04:07:04 +00:00
break;
}
2021-02-17 11:04:41 +00:00
}
}
2021-03-05 03:44:45 +00:00
2020-12-13 04:57:37 +00:00
pi.currentRequest = NULL;
}
2020-12-13 07:59:26 +00:00
if (mp.decoded.want_response && toUs) {
2021-03-12 06:10:36 +00:00
if (currentReply) {
printPacket("Sending response", currentReply);
2021-03-12 06:10:36 +00:00
service.sendToMesh(currentReply);
currentReply = NULL;
} else if(mp.from != ourNodeNum) {
// Note: if the message started with the local node we don't want to send a no response reply
2021-05-03 02:53:06 +00:00
2021-03-12 06:10:36 +00:00
// No one wanted to reply to this requst, tell the requster that happened
2022-12-30 02:41:37 +00:00
LOG_DEBUG("No one responded, send a nak\n");
2021-04-05 00:56:11 +00:00
// SECURITY NOTE! I considered sending back a different error code if we didn't find the psk (i.e. !isDecoded)
// but opted NOT TO. Because it is not a good idea to let remote nodes 'probe' to find out which PSKs were "good" vs
// bad.
2022-02-27 10:21:02 +00:00
routingModule->sendAckNak(Routing_Error_NO_RESPONSE, getFrom(&mp), mp.id, mp.channel);
2021-03-12 06:10:36 +00:00
}
2021-03-05 03:44:45 +00:00
}
2022-02-27 10:21:02 +00:00
if (!moduleFound)
2022-12-30 02:41:37 +00:00
LOG_DEBUG("No modules interested in portnum=%d, src=%s\n",
mp.decoded.portnum,
(src == RX_SRC_LOCAL) ? "LOCAL":"REMOTE");
2020-12-07 02:18:11 +00:00
}
MeshPacket *MeshModule::allocReply()
{
auto r = myReply;
myReply = NULL; // Only use each reply once
return r;
}
2020-12-07 02:18:11 +00:00
/** Messages can be received that have the want_response bit set. If set, this callback will be invoked
* so that subclasses can (optionally) send a response back to the original sender. Implementing this method
* is optional
*/
void MeshModule::sendResponse(const MeshPacket &req)
2021-03-05 03:44:45 +00:00
{
2020-12-07 02:18:11 +00:00
auto r = allocReply();
2021-03-05 03:44:45 +00:00
if (r) {
2020-12-07 02:18:11 +00:00
setReplyTo(r, req);
currentReply = r;
2021-03-05 03:44:45 +00:00
} else {
// Ignore - this is now expected behavior for routing module (because it ignores some replies)
2022-12-30 02:41:37 +00:00
// LOG_WARN("Client requested response but this module did not provide\n");
2020-12-07 02:18:11 +00:00
}
}
2021-03-05 03:44:45 +00:00
/** set the destination and packet parameters of packet p intended as a reply to a particular "to" packet
2020-12-07 02:18:11 +00:00
* This ensures that if the request packet was sent reliably, the reply is sent that way as well.
2021-03-05 03:44:45 +00:00
*/
void setReplyTo(MeshPacket *p, const MeshPacket &to)
{
assert(p->which_payload_variant == MeshPacket_decoded_tag); // Should already be set by now
p->to = getFrom(&to); // Make sure that if we are sending to the local node, we use our local node addr, not 0
p->channel = to.channel; // Use the same channel that the request came in on
// No need for an ack if we are just delivering locally (it just generates an ignored ack)
p->want_ack = (to.from != 0) ? to.want_ack : false;
if (p->priority == MeshPacket_Priority_UNSET)
p->priority = MeshPacket_Priority_RELIABLE;
2021-02-26 12:36:22 +00:00
p->decoded.request_id = to.id;
}
std::vector<MeshModule *> MeshModule::GetMeshModulesWithUIFrames()
2021-03-05 03:44:45 +00:00
{
std::vector<MeshModule *> modulesWithUIFrames;
2022-02-27 09:49:24 +00:00
if (modules) {
for (auto i = modules->begin(); i != modules->end(); ++i) {
auto &pi = **i;
if (pi.wantUIFrame()) {
2022-12-30 02:41:37 +00:00
LOG_DEBUG("Module wants a UI Frame\n");
2022-02-27 10:21:02 +00:00
modulesWithUIFrames.push_back(&pi);
}
}
}
2022-02-27 10:21:02 +00:00
return modulesWithUIFrames;
}
2022-01-13 08:19:36 +00:00
void MeshModule::observeUIEvents(
2022-01-13 08:19:36 +00:00
Observer<const UIFrameEvent *> *observer)
{
2022-02-27 09:49:24 +00:00
if (modules) {
for (auto i = modules->begin(); i != modules->end(); ++i) {
auto &pi = **i;
Observable<const UIFrameEvent *> *observable =
pi.getUIFrameObservable();
if (observable != NULL) {
2022-12-30 02:41:37 +00:00
LOG_DEBUG("Module wants a UI Frame\n");
observer->observe(observable);
}
}
}
}
AdminMessageHandleResult MeshModule::handleAdminMessageForAllPlugins(const MeshPacket &mp, AdminMessage *request, AdminMessage *response)
{
AdminMessageHandleResult handled = AdminMessageHandleResult::NOT_HANDLED;
2022-02-27 09:49:24 +00:00
if (modules) {
for (auto i = modules->begin(); i != modules->end(); ++i) {
auto &pi = **i;
2022-02-27 10:21:02 +00:00
AdminMessageHandleResult h = pi.handleAdminMessageForModule(mp, request, response);
if (h == AdminMessageHandleResult::HANDLED_WITH_RESPONSE)
{
// In case we have a response it always has priority.
2022-12-30 02:41:37 +00:00
LOG_DEBUG("Reply prepared by module '%s' of variant: %d\n",
pi.name,
response->which_payload_variant);
handled = h;
}
else if ((handled != AdminMessageHandleResult::HANDLED_WITH_RESPONSE) &&
(h == AdminMessageHandleResult::HANDLED))
{
// In case the message is handled it should be populated, but will not overwrite
// a result with response.
handled = h;
}
2022-01-13 08:19:36 +00:00
}
}
return handled;
2022-01-13 08:19:36 +00:00
}