This commit is contained in:
Kevin Hester 2021-04-03 16:06:40 +08:00
parent 8890ca759d
commit f3f09f0dcf
8 changed files with 78 additions and 19 deletions

View File

@ -1 +1 @@
mosquitto_sub -h test.mosquitto.org -v -t mstat/\# -t mesh/\# mosquitto_sub -h test.mosquitto.org -v -t mesh/\#

View File

@ -1 +1 @@
mosquitto_pub -h test.mosquitto.org -t mstat/FakeNode -m online -d mosquitto_pub -h test.mosquitto.org -t mesh/stat/FakeNode -m online -d

View File

@ -55,18 +55,22 @@ You probably don't care about this section - skip to the next one.
* DONE have sim provide a fake wifi connection status saying connected * DONE have sim provide a fake wifi connection status saying connected
* DONE don't start MQTT if we don't have wifi connected * DONE don't start MQTT if we don't have wifi connected
* have plugin send uplinks from mesh
* have plugin send downlinks to mesh
* don't decrypt messages before uplinking them to MQTT (move out of plugin)
* mqtt.meshtastic.org should have VERY basic auth at launch (to prevent abuse) * mqtt.meshtastic.org should have VERY basic auth at launch (to prevent abuse)
* make a GlobalChat channel as an initial test (with a well known AES128 key), figure out how globally unique IDs work * make a GlobalChat channel as an initial test (with a well known AES128 key), figure out how globally unique IDs work
* Give place in android app for users to select which channel they are sending on (and which channels they are watching) * Give place in android app for users to select which channel they are sending on (and which channels they are watching)
* attempt reconnect to server if internet connectivity changes * attempt reconnect to server if internet connectivity changes
* don't bother contacting server if we don't have any uplink/downlink channels * don't bother contacting server if we don't have any uplink/downlink channels
* test on ESP32 * DONE test on ESP32
* no need for python gateway to web initially: because both the simulator and ESP32 can talk wifi directly * no need for python gateway to web initially: because both the simulator and ESP32 can talk wifi directly
* if simmesh_name is set in preferences, create the MQTTSimInterface using that as the global channel_id * if simmesh_name is set in preferences, create the MQTTSimInterface using that as the global channel_id
* figure out how to use MQTT for simulator mesh network, use a special simmesh_name global channel_id? (because this would allow all nodes in simnet_xxx to subscribe only to those packets) * figure out how to use MQTT for simulator mesh network, use a special simmesh_name global channel_id? (because this would allow all nodes in simnet_xxx to subscribe only to those packets)
* do initial development inside of portduino * figure out legality of hosting public mqtt servers with chat msgs
* do as much possible on the device side (so we can eventually just have ESP32 talk directly to server) * DONE do initial development inside of portduino
* add mqtt_server to radio prefs * DONE do as much possible on the device side (so we can eventually just have ESP32 talk directly to server)
* DONE add mqtt_server to radio prefs
* eventually add a MQTTPacket on the ToRadio & FromRadio links * eventually add a MQTTPacket on the ToRadio & FromRadio links
* LATER: an android gateway would be useful * LATER: an android gateway would be useful

View File

@ -70,19 +70,19 @@ Any gateway-device will contact the MQTT broker.
### Topics ### Topics
* The "/mesh/crypt/CHANNELID/NODEID/PORTID" [topic](https://www.hivemq.com/blog/mqtt-essentials-part-5-mqtt-topics-best-practices/) will be used for messages sent from/to a mesh. * The "/mesh/crypt/CHANNELID/NODEID" [topic](https://www.hivemq.com/blog/mqtt-essentials-part-5-mqtt-topics-best-practices/) will be used for (encrypted) messages sent from/to a mesh.
Gateway nodes will foward any MeshPacket from a local mesh channel with uplink_enabled. The packet (encapsulated in a ServiceEnvelope) will remain encrypted with the key for the specified channel. Gateway nodes will foward any MeshPacket from a local mesh channel with uplink_enabled. The packet (encapsulated in a ServiceEnvelope) will remain encrypted with the key for the specified channel.
For any channels in the gateway node with downlink_enabled, the gateway node will forward packets from MQTT to the local mesh. It will do this by subscribing to mesh/crypt/CHANNELID/# and forwarding relevant packets. For any channels in the gateway node with downlink_enabled, the gateway node will forward packets from MQTT to the local mesh. It will do this by subscribing to mesh/crypt/CHANNELID/# and forwarding relevant packets.
If the channelid 'well known'/public it could be decrypted by a web service (if the web service was provided with the associated channel key), in which case it will be decrypted by a web service and appear at "mesh/clear/CHANNELID/NODEID/PORTID". Note: This is not in the initial deliverable. * If the channelid 'well known'/public it could be decrypted by a web service (if the web service was provided with the associated channel key), in which case it will be decrypted by a web service and appear at "mesh/clear/CHANNELID/NODEID/PORTID". Note: This is not in the initial deliverable.
FIXME, consider how text message global mirroring could scale (or not) FIXME, consider how text message global mirroring could scale (or not)
FIXME, possibly don't global mirror text messages - instead rely on matrix/riot? FIXME, possibly don't global mirror text messages - instead rely on matrix/riot?
FIXME, consider possible attacks by griefers and how they can be prvented FIXME, consider possible attacks by griefers and how they can be prvented
* The "/mstat/NODEID" topic contains a simple string showing connection status of nodes. We rely on the MQTT feature for automatically publishing special failrue messages to this topic when the device disconnects. * The "/mesh/stat/NODEID" topic contains a simple string showing connection status of nodes. We rely on the MQTT feature for automatically publishing special failrue messages to this topic when the device disconnects.
#### Service Envelope #### Service Envelope

View File

@ -1,12 +1,13 @@
#include "MQTT.h" #include "MQTT.h"
#include "MQTTPlugin.h"
#include "NodeDB.h" #include "NodeDB.h"
#include "mesh/generated/mqtt.pb.h"
#include <WiFi.h> #include <WiFi.h>
#include <assert.h> #include <assert.h>
MQTT *mqtt; MQTT *mqtt;
String statusTopic = "mstat/"; String statusTopic = "mesh/stat/";
String packetTopic = "mesh/";
void mqttCallback(char *topic, byte *payload, unsigned int length) void mqttCallback(char *topic, byte *payload, unsigned int length)
{ {
@ -23,8 +24,10 @@ void mqttInit()
DEBUG_MSG("MQTT disabled...\n"); DEBUG_MSG("MQTT disabled...\n");
else if (!WiFi.isConnected()) else if (!WiFi.isConnected())
DEBUG_MSG("WiFi is not connected, can not start MQTT\n"); DEBUG_MSG("WiFi is not connected, can not start MQTT\n");
else else {
new MQTT(); new MQTT();
new MQTTPlugin();
}
} }
MQTT::MQTT() : pubSub(mqttClient) MQTT::MQTT() : pubSub(mqttClient)
@ -59,18 +62,35 @@ MQTT::MQTT() : pubSub(mqttClient)
} }
} }
void MQTT::publish(const MeshPacket *mp) void MQTT::publish(const MeshPacket &mp)
{ {
// DEBUG_MSG("publish %s = %s\n", suffix.c_str(), payload.c_str()); // don't bother sending if not connected...
if (pubSub.connected()) {
// FIXME - check uplink enabled
// pubSub.publish(getTopic(suffix), payload.c_str(), retained); const char *channelId = "fixmechan";
ServiceEnvelope env = ServiceEnvelope_init_default;
env.channel_id = (char *)channelId;
env.gateway_id = owner.id;
env.packet = (MeshPacket *)&mp;
// FIXME - this size calculation is super sloppy, but it will go away once we dynamically alloc meshpackets
static uint8_t bytes[MeshPacket_size + 64];
size_t numBytes = pb_encode_to_bytes(bytes, sizeof(bytes), ServiceEnvelope_fields, &env);
const char *topic = getCryptTopic(channelId);
DEBUG_MSG("publish %s, %u bytes\n", topic, numBytes);
pubSub.publish(topic, bytes, numBytes, false);
}
} }
const char *MQTT::getTopic(String suffix, const char *direction) const char *MQTT::getCryptTopic(const char *channelId)
{ {
static char buf[128]; static char buf[128];
// "mesh/crypt/CHANNELID/NODEID/PORTID" // "mesh/crypt/CHANNELID/NODEID/PORTID"
snprintf(buf, sizeof(buf), "mesh/%s/%s/%s", direction, owner.id, suffix.c_str()); snprintf(buf, sizeof(buf), "mesh/crypt/%s/%s", channelId, owner.id);
return buf; return buf;
} }

View File

@ -23,10 +23,10 @@ class MQTT
/** /**
* Publish a packet on the glboal MQTT server. * Publish a packet on the glboal MQTT server.
*/ */
void publish(const MeshPacket *mp); void publish(const MeshPacket &mp);
private: private:
const char *getTopic(String suffix, const char *direction = "dev"); const char *getCryptTopic(const char *channelId);
}; };
void mqttInit(); void mqttInit();

18
src/mqtt/MQTTPlugin.cpp Normal file
View File

@ -0,0 +1,18 @@
#include "MQTTPlugin.h"
#include "MQTT.h"
#include "MeshService.h"
#include "NodeDB.h"
#include "Router.h"
#include "configuration.h"
#include "main.h"
MQTTPlugin::MQTTPlugin() : MeshPlugin("mqtt")
{
isPromiscuous = true; // We always want to update our nodedb, even if we are sniffing on others
}
bool MQTTPlugin::handleReceived(const MeshPacket &mp)
{
mqtt->publish(mp);
return false; // never claim handled
}

17
src/mqtt/MQTTPlugin.h Normal file
View File

@ -0,0 +1,17 @@
#pragma once
#include "MeshPlugin.h"
/**
* NodeInfo plugin for sending/receiving NodeInfos into the mesh
*/
class MQTTPlugin : public MeshPlugin
{
public:
MQTTPlugin();
protected:
/** We sniff all packets */
virtual bool handleReceived(const MeshPacket &mp);
virtual bool wantPacket(const MeshPacket *p) { return true; }
};