Merge branch 'master' into master

This commit is contained in:
Jm Casler
2021-03-25 19:32:13 -07:00
committed by GitHub
24 changed files with 343 additions and 116 deletions

View File

@@ -33,7 +33,7 @@ int16_t Channels::generateHash(ChannelIndex channelNum)
return -1; // invalid
else {
const char *name = getName(channelNum);
uint8_t h = xorHash((const uint8_t *) name, strlen(name));
uint8_t h = xorHash((const uint8_t *)name, strlen(name));
h ^= xorHash(k.bytes, k.length);
@@ -184,7 +184,7 @@ void Channels::onConfigChanged()
Channel &Channels::getByIndex(ChannelIndex chIndex)
{
assert(chIndex < channelFile.channels_count);
assert(chIndex < channelFile.channels_count); // This should be equal to MAX_NUM_CHANNELS
Channel *ch = channelFile.channels + chIndex;
return *ch;
}
@@ -278,11 +278,11 @@ const char *Channels::getPrimaryName()
*/
bool Channels::decryptForHash(ChannelIndex chIndex, ChannelHash channelHash)
{
if(chIndex > getNumChannels() || getHash(chIndex) != channelHash) {
// DEBUG_MSG("Skipping channel %d (hash %x) due to invalid hash/index, want=%x\n", chIndex, getHash(chIndex), channelHash);
if (chIndex > getNumChannels() || getHash(chIndex) != channelHash) {
// DEBUG_MSG("Skipping channel %d (hash %x) due to invalid hash/index, want=%x\n", chIndex, getHash(chIndex),
// channelHash);
return false;
}
else {
} else {
DEBUG_MSG("Using channel %d (hash 0x%x)\n", chIndex, channelHash);
setCrypto(chIndex);
return true;

View File

@@ -31,6 +31,39 @@ MeshPlugin::~MeshPlugin()
assert(0); // FIXME - remove from list of plugins once someone needs this feature
}
MeshPacket *MeshPlugin::allocAckNak(Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex)
{
Routing c = Routing_init_default;
c.error_reason = err;
// 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_fields, &c);
p->priority = MeshPacket_Priority_ACK;
p->hop_limit = 0; // Assume just immediate neighbors for now
p->to = to;
p->decoded.request_id = idFrom;
p->channel = chIndex;
DEBUG_MSG("Alloc an err=%d,to=0x%x,idFrom=0x%x,id=0x%x\n", err, to, idFrom, p->id);
return p;
}
MeshPacket *MeshPlugin::allocErrorResponse(Routing_Error err, const MeshPacket *p)
{
auto r = allocAckNak(err, getFrom(p), p->id, p->channel);
setReplyTo(r, *p);
return r;
}
void MeshPlugin::callPlugins(const MeshPacket &mp)
{
// DEBUG_MSG("In call plugins\n");
@@ -52,33 +85,47 @@ void MeshPlugin::callPlugins(const MeshPacket &mp)
auto ch = channels.getByIndex(mp.channel);
assert(ch.has_settings);
/// Is the channel this packet arrived on acceptable? (security check)
bool rxChannelOk = !pi.boundChannel || (mp.from == 0) || (strcmp(ch.settings.name, pi.boundChannel) == 0);
/// We only call plugins that are interested in the packet (and the message is destined to us or we are promiscious)
bool wantsPacket = rxChannelOk && (pi.isPromiscuous || toUs) && pi.wantPacket(&mp);
// DEBUG_MSG("Plugin %s wantsPacket=%d\n", pi.name, wantsPacket);
bool wantsPacket = (pi.isPromiscuous || toUs) && pi.wantPacket(&mp);
if (wantsPacket) {
// DEBUG_MSG("Plugin %s wantsPacket=%d\n", pi.name, wantsPacket);
pluginFound = true;
bool handled = pi.handleReceived(mp);
/// Is the channel this packet arrived on acceptable? (security check)
bool rxChannelOk = !pi.boundChannel || (mp.from == 0) || (strcmp(ch.settings.name, pi.boundChannel) == 0);
// 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 plugin send a reply, once that happens, remaining plugins are not considered
if (!rxChannelOk) {
// no one should have already replied!
assert(!currentReply);
// 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);
DEBUG_MSG("Plugin %s sent a response\n", pi.name);
if (mp.decoded.want_response) {
DEBUG_MSG("packet on wrong channel, returning error\n");
currentReply = pi.allocErrorResponse(Routing_Error_NOT_AUTHORIZED, &mp);
} else
DEBUG_MSG("packet on wrong channel, but client didn't want response\n");
} else {
DEBUG_MSG("Plugin %s considered\n", pi.name);
}
if (handled) {
DEBUG_MSG("Plugin %s handled and skipped other processing\n", pi.name);
break;
bool handled = pi.handleReceived(mp);
// 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 plugin send a reply, once that happens, remaining plugins are not
// 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);
DEBUG_MSG("Plugin %s sent a response\n", pi.name);
} else {
DEBUG_MSG("Plugin %s considered\n", pi.name);
}
if (handled) {
DEBUG_MSG("Plugin %s handled and skipped other processing\n", pi.name);
break;
}
}
}
@@ -90,8 +137,7 @@ void MeshPlugin::callPlugins(const MeshPacket &mp)
DEBUG_MSG("Sending response\n");
service.sendToMesh(currentReply);
currentReply = NULL;
}
else {
} else {
// No one wanted to reply to this requst, tell the requster that happened
DEBUG_MSG("No one responded, send a nak\n");
routingPlugin->sendAckNak(Routing_Error_NO_RESPONSE, getFrom(&mp), mp.id, mp.channel);

View File

@@ -1,5 +1,6 @@
#pragma once
#include "mesh/Channels.h"
#include "mesh/MeshTypes.h"
#include <vector>
@@ -90,6 +91,11 @@ class MeshPlugin
*/
virtual bool wantUIFrame() { return false; }
MeshPacket *allocAckNak(Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex);
/// Send an error response for the specified packet.
MeshPacket *allocErrorResponse(Routing_Error err, const MeshPacket *p);
private:
/**
* If any of the current chain of plugins has already sent a reply, it will be here. This is useful to allow

View File

@@ -83,16 +83,17 @@ bool NodeDB::resetRadioConfig()
radioGeneration++;
radioConfig.has_preferences = true;
if (radioConfig.preferences.factory_reset) {
DEBUG_MSG("Performing factory reset!\n");
installDefaultDeviceState();
didFactoryReset = true;
} else if (channelFile.channels_count == 0) {
}
if (channelFile.channels_count != MAX_NUM_CHANNELS) {
DEBUG_MSG("Setting default channel and radio preferences!\n");
channels.initDefaults();
radioConfig.has_preferences = true;
}
channels.onConfigChanged();
@@ -451,6 +452,8 @@ void NodeDB::updatePosition(uint32_t nodeId, const Position &p)
info->position.latitude_i = p.latitude_i;
info->position.longitude_i = p.longitude_i;
}
if (p.altitude)
info->position.altitude = p.altitude;
info->has_position = true;
updateGUIforNode = info;
notifyObservers(true); // Force an update whether or not our node counts have changed
@@ -486,8 +489,7 @@ void NodeDB::updateUser(uint32_t nodeId, const User &p)
/// we updateGUI and updateGUIforNode if we think our this change is big enough for a redraw
void NodeDB::updateFrom(const MeshPacket &mp)
{
if (mp.which_payloadVariant == MeshPacket_decoded_tag) {
if (mp.which_payloadVariant == MeshPacket_decoded_tag && mp.from) {
DEBUG_MSG("Update DB node 0x%x, rx_time=%u\n", mp.from, mp.rx_time);
NodeInfo *info = getOrCreateNode(getFrom(&mp));
@@ -498,7 +500,8 @@ void NodeDB::updateFrom(const MeshPacket &mp)
info->position.time = mp.rx_time;
}
info->snr = mp.rx_snr; // keep the most recent SNR we received for this node.
if (mp.rx_snr)
info->snr = mp.rx_snr; // keep the most recent SNR we received for this node.
}
}

View File

@@ -115,7 +115,8 @@ void Router::abortSendAndNak(Routing_Error err, MeshPacket *p)
packetPool.release(p);
}
void Router::setReceivedMessage() {
void Router::setReceivedMessage()
{
setInterval(0); // Run ASAP, so we can figure out our correct sleep time
}
@@ -123,9 +124,13 @@ ErrorCode Router::sendLocal(MeshPacket *p)
{
// No need to deliver externally if the destination is the local node
if (p->to == nodeDB.getNodeNum()) {
printPacket("Enqueuing local", p);
fromRadioQueue.enqueue(p);
setReceivedMessage();
if (fromRadioQueue.enqueue(p, 0)) {
printPacket("Enqueued local", p);
setReceivedMessage();
} else {
printPacket("BUG! fromRadioQueue is full! Discarding!", p);
packetPool.release(p);
}
return ERRNO_OK;
} else if (!iface) {
// We must be sending to remote nodes also, fail if no interface found
@@ -143,9 +148,10 @@ ErrorCode Router::sendLocal(MeshPacket *p)
}
}
void printBytes(const char *label, const uint8_t *p, size_t numbytes) {
void printBytes(const char *label, const uint8_t *p, size_t numbytes)
{
DEBUG_MSG("%s: ", label);
for(size_t i = 0; i < numbytes; i++)
for (size_t i = 0; i < numbytes; i++)
DEBUG_MSG("%02x ", p[i]);
DEBUG_MSG("\n");
}
@@ -189,7 +195,7 @@ ErrorCode Router::send(MeshPacket *p)
return ERRNO_TOO_LARGE;
}
//printBytes("plaintext", bytes, numbytes);
// printBytes("plaintext", bytes, numbytes);
auto hash = channels.setActiveByIndex(p->channel);
if (hash < 0) {
@@ -247,19 +253,18 @@ bool Router::perhapsDecode(MeshPacket *p)
rawSize); // we have to copy into a scratch buffer, because these bytes are a union with the decoded protobuf
crypto->decrypt(p->from, p->id, rawSize, bytes);
//printBytes("plaintext", bytes, p->encrypted.size);
// printBytes("plaintext", bytes, p->encrypted.size);
// Take those raw bytes and convert them back into a well structured protobuf we can understand
memset(&p->decoded, 0, sizeof(p->decoded));
memset(&p->decoded, 0, sizeof(p->decoded));
if (!pb_decode_from_bytes(bytes, rawSize, Data_fields, &p->decoded)) {
DEBUG_MSG("Invalid protobufs in received mesh packet (bad psk?)!\n");
} else if(p->decoded.portnum == PortNum_UNKNOWN_APP) {
} else if (p->decoded.portnum == PortNum_UNKNOWN_APP) {
DEBUG_MSG("Invalid portnum (bad psk?)!\n");
}
else {
} else {
// parsing was successful
p->which_payloadVariant = MeshPacket_decoded_tag; // change type to decoded
p->channel = chIndex; // change to store the index instead of the hash
p->channel = chIndex; // change to store the index instead of the hash
printPacket("decoded message", p);
return true;
}
@@ -285,7 +290,7 @@ void Router::handleReceived(MeshPacket *p)
p->rx_time = getValidTime(RTCQualityFromNet); // store the arrival timestamp for the phone
// Take those raw bytes and convert them back into a well structured protobuf we can understand
bool decoded = perhapsDecode(p);
bool decoded = perhapsDecode(p);
if (decoded) {
// parsing was successful, queue for our recipient
printPacket("handleReceived", p);
@@ -293,18 +298,20 @@ void Router::handleReceived(MeshPacket *p)
// call any promiscious plugins here, make a (non promisiocous) plugin for forwarding messages to phone api
// sniffReceived(p);
MeshPlugin::callPlugins(*p);
} else {
DEBUG_MSG("packet decoding failed\n");
}
}
void Router::perhapsHandleReceived(MeshPacket *p)
{
assert(radioConfig.has_preferences);
bool ignore = is_in_repeated(radioConfig.preferences.ignore_incoming, getFrom(p));
bool ignore = is_in_repeated(radioConfig.preferences.ignore_incoming, p->from);
if (ignore)
DEBUG_MSG("Ignoring incoming message, 0x%x is in our ignore list\n", p->from);
else if (ignore |= shouldFilterReceived(p)) {
// DEBUG_MSG("Incoming message was filtered 0x%x\n", p->from);
DEBUG_MSG("Incoming message was filtered 0x%x\n", p->from);
}
// Note: we avoid calling shouldFilterReceived if we are supposed to ignore certain nodes - because some overrides might

View File

@@ -142,6 +142,7 @@ void SX1262Interface::addReceiveMetadata(MeshPacket *mp)
{
// DEBUG_MSG("PacketStatus %x\n", lora.getPacketStatus());
mp->rx_snr = lora.getSNR();
mp->rx_rssi = lround(lora.getRSSI());
}
/** We override to turn on transmitter power as needed.

View File

@@ -31,7 +31,9 @@ template <class T> class TypedQueue
bool isEmpty() { return uxQueueMessagesWaiting(h) == 0; }
bool enqueue(T x, TickType_t maxWait = portMAX_DELAY)
/** euqueue a packet. Also, maxWait used to default to portMAX_DELAY, but we now want to callers to THINK about what blocking
* they want */
bool enqueue(T x, TickType_t maxWait)
{
if (reader) {
reader->setInterval(0);

View File

@@ -125,7 +125,7 @@ extern const pb_msgdesc_t ChannelFile_msg;
/* Maximum encoded size of messages (where known) */
#define LegacyRadioConfig_size 4
#define LegacyRadioConfig_LegacyPreferences_size 2
#define DeviceState_size 4898
#define DeviceState_size 4920
#define ChannelFile_size 832
#ifdef __cplusplus

View File

@@ -57,7 +57,9 @@ typedef enum _Routing_Error {
Routing_Error_MAX_RETRANSMIT = 5,
Routing_Error_NO_CHANNEL = 6,
Routing_Error_TOO_LARGE = 7,
Routing_Error_NO_RESPONSE = 8
Routing_Error_NO_RESPONSE = 8,
Routing_Error_BAD_REQUEST = 32,
Routing_Error_NOT_AUTHORIZED = 33
} Routing_Error;
typedef enum _MeshPacket_Priority {
@@ -150,6 +152,7 @@ typedef struct _MeshPacket {
uint8_t hop_limit;
bool want_ack;
MeshPacket_Priority priority;
int32_t rx_rssi;
} MeshPacket;
typedef struct _NodeInfo {
@@ -206,8 +209,8 @@ typedef struct _ToRadio {
#define _CriticalErrorCode_ARRAYSIZE ((CriticalErrorCode)(CriticalErrorCode_Brownout+1))
#define _Routing_Error_MIN Routing_Error_NONE
#define _Routing_Error_MAX Routing_Error_NO_RESPONSE
#define _Routing_Error_ARRAYSIZE ((Routing_Error)(Routing_Error_NO_RESPONSE+1))
#define _Routing_Error_MAX Routing_Error_NOT_AUTHORIZED
#define _Routing_Error_ARRAYSIZE ((Routing_Error)(Routing_Error_NOT_AUTHORIZED+1))
#define _MeshPacket_Priority_MIN MeshPacket_Priority_UNSET
#define _MeshPacket_Priority_MAX MeshPacket_Priority_MAX
@@ -228,7 +231,7 @@ extern "C" {
#define RouteDiscovery_init_default {0, {0, 0, 0, 0, 0, 0, 0, 0}}
#define Routing_init_default {0, {RouteDiscovery_init_default}}
#define Data_init_default {_PortNum_MIN, {0, {0}}, 0, 0, 0, 0}
#define MeshPacket_init_default {0, 0, 0, 0, {Data_init_default}, 0, 0, 0, 0, 0, _MeshPacket_Priority_MIN}
#define MeshPacket_init_default {0, 0, 0, 0, {Data_init_default}, 0, 0, 0, 0, 0, _MeshPacket_Priority_MIN, 0}
#define NodeInfo_init_default {0, false, User_init_default, false, Position_init_default, 0}
#define MyNodeInfo_init_default {0, 0, 0, "", "", "", _CriticalErrorCode_MIN, 0, 0, 0, 0, 0}
#define LogRecord_init_default {"", 0, "", _LogRecord_Level_MIN}
@@ -239,7 +242,7 @@ extern "C" {
#define RouteDiscovery_init_zero {0, {0, 0, 0, 0, 0, 0, 0, 0}}
#define Routing_init_zero {0, {RouteDiscovery_init_zero}}
#define Data_init_zero {_PortNum_MIN, {0, {0}}, 0, 0, 0, 0}
#define MeshPacket_init_zero {0, 0, 0, 0, {Data_init_zero}, 0, 0, 0, 0, 0, _MeshPacket_Priority_MIN}
#define MeshPacket_init_zero {0, 0, 0, 0, {Data_init_zero}, 0, 0, 0, 0, 0, _MeshPacket_Priority_MIN, 0}
#define NodeInfo_init_zero {0, false, User_init_zero, false, Position_init_zero, 0}
#define MyNodeInfo_init_zero {0, 0, 0, "", "", "", _CriticalErrorCode_MIN, 0, 0, 0, 0, 0}
#define LogRecord_init_zero {"", 0, "", _LogRecord_Level_MIN}
@@ -291,6 +294,7 @@ extern "C" {
#define MeshPacket_hop_limit_tag 10
#define MeshPacket_want_ack_tag 11
#define MeshPacket_priority_tag 12
#define MeshPacket_rx_rssi_tag 13
#define NodeInfo_num_tag 1
#define NodeInfo_user_tag 2
#define NodeInfo_position_tag 3
@@ -362,7 +366,8 @@ X(a, STATIC, SINGULAR, FIXED32, rx_time, 7) \
X(a, STATIC, SINGULAR, FLOAT, rx_snr, 8) \
X(a, STATIC, SINGULAR, UINT32, hop_limit, 10) \
X(a, STATIC, SINGULAR, BOOL, want_ack, 11) \
X(a, STATIC, SINGULAR, UENUM, priority, 12)
X(a, STATIC, SINGULAR, UENUM, priority, 12) \
X(a, STATIC, SINGULAR, INT32, rx_rssi, 13)
#define MeshPacket_CALLBACK NULL
#define MeshPacket_DEFAULT NULL
#define MeshPacket_payloadVariant_decoded_MSGTYPE Data
@@ -454,12 +459,12 @@ extern const pb_msgdesc_t ToRadio_msg;
#define RouteDiscovery_size 40
#define Routing_size 42
#define Data_size 260
#define MeshPacket_size 298
#define MeshPacket_size 309
#define NodeInfo_size 126
#define MyNodeInfo_size 89
#define LogRecord_size 81
#define FromRadio_size 307
#define ToRadio_size 301
#define FromRadio_size 318
#define ToRadio_size 312
#ifdef __cplusplus
} /* extern "C" */

View File

@@ -77,7 +77,8 @@ void registerHandlers(HTTPServer *insecureServer, HTTPSServer *secureServer)
ResourceNode *nodeAPIv1ToRadio = new ResourceNode("/api/v1/toradio", "PUT", &handleAPIv1ToRadio);
ResourceNode *nodeAPIv1FromRadio = new ResourceNode("/api/v1/fromradio", "GET", &handleAPIv1FromRadio);
ResourceNode *nodeHotspot = new ResourceNode("/hotspot-detect.html", "GET", &handleHotspot);
ResourceNode *nodeHotspotApple = new ResourceNode("/hotspot-detect.html", "GET", &handleHotspot);
ResourceNode *nodeHotspotAndroid = new ResourceNode("/generate_204", "GET", &handleHotspot);
ResourceNode *nodeFavicon = new ResourceNode("/favicon.ico", "GET", &handleFavicon);
ResourceNode *nodeRoot = new ResourceNode("/", "GET", &handleRoot);
ResourceNode *nodeStaticBrowse = new ResourceNode("/static", "GET", &handleStaticBrowse);
@@ -96,7 +97,8 @@ void registerHandlers(HTTPServer *insecureServer, HTTPSServer *secureServer)
secureServer->registerNode(nodeAPIv1ToRadioOptions);
secureServer->registerNode(nodeAPIv1ToRadio);
secureServer->registerNode(nodeAPIv1FromRadio);
secureServer->registerNode(nodeHotspot);
secureServer->registerNode(nodeHotspotApple);
secureServer->registerNode(nodeHotspotAndroid);
secureServer->registerNode(nodeFavicon);
secureServer->registerNode(nodeRoot);
secureServer->registerNode(nodeStaticBrowse);
@@ -117,7 +119,8 @@ void registerHandlers(HTTPServer *insecureServer, HTTPSServer *secureServer)
insecureServer->registerNode(nodeAPIv1ToRadioOptions);
insecureServer->registerNode(nodeAPIv1ToRadio);
insecureServer->registerNode(nodeAPIv1FromRadio);
insecureServer->registerNode(nodeHotspot);
insecureServer->registerNode(nodeHotspotApple);
insecureServer->registerNode(nodeHotspotAndroid);
insecureServer->registerNode(nodeFavicon);
insecureServer->registerNode(nodeRoot);
insecureServer->registerNode(nodeStaticBrowse);