Controller - ESPEasy P2P Networking¶
.
Controller details¶
Type: Controller
Name: ESPEasy P2P Networking
Status: NORMAL
GitHub: C013.cpp
Maintainer: TD-er
Change log¶
Changed in version 2.0: …
improved Implementation of secure communication and check for valid data.
Added in version 1.0: …
added Initial release version.
Description¶
ESPEasy is able to communicate between nodes itself. It is an IANA registered service: espeasy-p2p
Service Name: espeasy-p2p
Port Number: 8266
Transport Protocol: UDP
Description: ESPeasy peer-2-peer communication
Registration date: 2018-11-28
This protocol is targeted specific for use by ESPeasy to let ESPeasy nodes communicate with each other to create a big swarm of nodes working together without the need for a hosted service like MQTT, which needs a central broker.
It is currently used for:
Discovery of nodes
Sharing sensor data among nodes
Later updates may add:
Distribution of settings
Sending commands
Sending & Known Nodes¶
ESPEasy keeps track of all nodes advertising themselves via Sysinfo messages.
This knowledge is kept in a NodeStruct
for at least 10 minutes.
If a node is not sending a Sysinfo message in this period, it will be removed from the list.
Data Format Versions¶
During the IANA port assignment assessment, a number of issues were pointed out by their experts.
Versioning
Security
Data validation
Traffic limiting and congestion handling
There are now 2 versions available:
Version “0” - No security, no data validation.
Version “1” - Introduced in ESPeasy build <???>
Data Format Version 0¶
Sending and receiving is causing issues when the swarm of nodes increases.
All nodes with this service enabled will advertise their presence every 30 seconds via broadcast
Nodes can not subscribe to receiving sensor data updates
Non broadcast messages are sent to each individual known node, regardless if the receiving node will use the data
Sensor Data messages are sent to each individual known node
Sensor Info updates are sent to each individual known node when a plugin coupled to this plugin is saved.
Of each known node the following data is kept:
struct NodeStruct
{
String nodeName;
byte ip[4];
uint16_t build;
byte age;
byte nodeType;
};
The key to index this NodeStruct
is the nodes unit number.
ASCII Data¶
Command Message¶
First byte is not 0xFF.
The entire message processed as a command like this:
packetBuffer[len] = 0;
ExecuteCommand_all(EventValueSource::Enum::VALUE_SOURCE_SYSTEM, &packetBuffer[0]);
As can be seen, no checks for size, and it is just expected to be a valid ESPeasy command. Also no check to see if the command is supported by the receiving end and no feedback to the sender.
Binary Data¶
Binary data is marked with the first byte 0xFF.
On the receiving end, it is packed in an event in the Data
field and processed like this:
struct EventStruct TempEvent;
TempEvent.Data = reinterpret_cast<byte*>(&packetBuffer[0]);
TempEvent.Par1 = remoteIP[3];
TempEvent.Par2 = len;
PluginCall(PLUGIN_UDP_IN, &TempEvent, dummyString);
CPluginCall(CPLUGIN_UDP_IN, &TempEvent);
N.B. only the controller C013 implements code for handling UDP data.
Message types supported, determined by the 2nd byte:
1: Sysinfo message
2: Sensor info pull request (not implemented)
3: Sensor info
4: Sensor data pull request (not implemented)
5: Sensor data
Sysinfo Message¶
There are 2 types of Sysinfo messages, a standard and an extended message. The extended message starts with the same information as the standard one.
Standard Sysinfo message (13 bytes):
2 bytes marker (255 , 1)
6 byte MAC address
4 byte IP address
1 byte unit number
Extended Sysinfo message (13 + 28 = 41 bytes):
2 bytes ESPeasy data version number (LSB, MSB)
25 bytes node name
1 byte node type
The node type is defined as:
1 = “ESP Easy”
5 = “Rpi Easy”
17 = “ESP Easy Mega”
33 = “ESP Easy 32”
34 = “ESP Easy 32-S2”
35 = “ESP Easy 32-C3”
36 = “ESP Easy 32-S3”
37 = “ESP Easy 32-C2”
38 = “ESP Easy 32-H2”
65 = “Arduino Easy”
81 = “Nano Easy”
Sensor Info message¶
Sensor Info messages are just a description of a shared sensor. It contains some information to setup a new sensor on the receiving end.
These messages are just a serialized byte stream of struct C013_SensorInfoStruct
.
struct C013_SensorInfoStruct
{
byte header = 255;
byte ID = 3;
byte sourceUnit;
byte destUnit;
byte sourceTaskIndex;
byte destTaskIndex;
byte deviceNumber;
char taskName[26];
char ValueNames[VARS_PER_TASK][26];
};
Sensor Data message¶
These messages are just a serialized byte stream of struct C013_SensorDataStruct
.
struct C013_SensorDataStruct
{
byte header = 255;
byte ID = 5;
byte sourceUnit;
byte destUnit;
byte sourceTaskIndex;
byte destTaskIndex;
float Values[VARS_PER_TASK];
};
Data Format Version 1¶
This version remains compatible with version 0 for backwards compatibility. It is using the “next” unused marker.
All messages will have a standard packet data format:
2 bytes Marker (255 , 6)
2 bytes Version => also determines data offset (header length)
2 bytes Message type
2 bytes Size of data block in “N” blocks of 16 bytes
2 bytes Key/group selector
2 bytes Sequence number
(16 x N) bytes Data block AES encrypted data (including 2 bytes checksum)
2 bytes Packet checksum
This allows to:
Distinguish data format versions
Filter on message type before allocating large buffers
Use multiple (pre-shared) encryption keys to have several levels of security or just several groups.
Validate correct transmission of packet (last 2 checksum bytes) before decrypting data.
Allow for larger messages to be sent in sequences. (e.g. firmware upgrades?)
Validate sender and content of data block, since it contains a checksum too, which is part of the encrypted data block.
Since AES has a block size of 16 bytes (128 bit), the size of the data block is defined as a block of 16 bytes. This allows up-to 1 MB of messages. (2^16 * 2^4 = 2^20) An UDP datagram sent over IPv4 cannot exceed 65,507 bytes (65,535 - 8 byte UDP header - 20 byte IP header). In IPv6 jumbograms it is possible to have UDP packets of size greater than 65,535 bytes.