Changed the library to not use AutoAcknowledge so that an analysis of the network can not be done by a simple trying to ping a device/master. The master must be paired to a device before he sends any ACK package or must enable new slaves to answer to a MASTER_SEARCH Request. The AutoAcknowledge feature would automatically send an ACK package which makes the master and any other device directly visible.

Be aware that this feature does not prevent a devices for being detected by sniffing the radio traffic, because the normal data traffic is still there and not encrypted in any way. An Encryption would hide the sender address. This would make it again more difficult to copy a sender address and simulate a paired device. (This is stikk possible now). But again, this will come later, maybe!
The best solution is to implement an encryption feature at a higher communication layer.
This commit is contained in:
Marcel Schulz 2017-12-15 13:55:09 +01:00
parent 21e31893f2
commit a0cb35706c
6 changed files with 163 additions and 128 deletions

32
src/Device.cpp Normal file
View File

@ -0,0 +1,32 @@
#include "SBDevice.h"
SBMasterStorage SBMasterStorage::initialize() {
SBMasterStorage storage;
#if defined(ESP8266)
EEPROM.begin(SB_NETWORK_FLASH_SIZE);
#endif
EEPROM.get(0 + sizeof(SBNetworkDevice), storage);
if (storage.ID[0] != 'M' || storage.ID[1] != 'S') {
// We have to create a new one
storage.ID[0] = 'M';
storage.ID[1] = 'S';
Serial.println("Creating new Master Storage");
EEPROM.put(0 + sizeof(SBNetworkDevice), storage);
#if defined(ESP8266)
EEPROM.commit();
EEPROM.end();
#endif
}
return storage;
}
void SBMasterStorage::save() {
#if defined(ESP8266)
EEPROM.begin(SB_NETWORK_FLASH_SIZE);
#endif
EEPROM.put(sizeof(SBNetworkDevice), *this);
#if defined(ESP8266)
EEPROM.commit();
EEPROM.end();
#endif
}

View File

@ -18,36 +18,11 @@ public:
char ID[2] = { 'M', 'S' }; // MS stands for master storage char ID[2] = { 'M', 'S' }; // MS stands for master storage
SBMacAddress Slaves[MAX_CLIENTS]; SBMacAddress Slaves[MAX_CLIENTS];
static SBMasterStorage initialize(){ static SBMasterStorage initialize();
SBMasterStorage storage;
#if defined(ESP8266)
EEPROM.begin(FLASH_SIZE);
#endif
EEPROM.get(0 + sizeof(SBNetworkDevice), storage);
if (storage.ID[0] != 'M' || storage.ID[1] != 'S'){
// We have to create a new one
storage.ID[0] = 'M';
storage.ID[1] = 'S';
Serial.println("Creating new Master Storage");
EEPROM.put(0 + sizeof(SBNetworkDevice), storage);
#if defined(ESP8266)
EEPROM.commit();
EEPROM.end();
#endif
}
return storage;
}
void save(){ void save();
#if defined(ESP8266)
EEPROM.begin(FLASH_SIZE);
#endif
EEPROM.put(0 + sizeof(SBNetworkDevice), *this);
#if defined(ESP8266)
EEPROM.commit();
EEPROM.end();
#endif
}
}; };
#define SB_NETWORK_FLASH_SIZE (sizeof(SBNetworkDevice) + sizeof(SBMasterStorage))
#endif #endif

View File

@ -62,7 +62,7 @@ void SBNetwork::initialize(SBMacAddress mac){
this->radio.enableDynamicPayloads(); this->radio.enableDynamicPayloads();
//this->radio.enableDynamicAck(); //this->radio.enableDynamicAck();
this->radio.setAutoAck(true); this->radio.setAutoAck(false); // We use our own ack handling
//this->radio.enableAckPayload(); //this->radio.enableAckPayload();
this->radio.setRetries(40, 5); this->radio.setRetries(40, 5);
@ -89,7 +89,7 @@ void SBNetwork::initialize(SBMacAddress mac){
void SBNetwork::initializeNetworkDevice(SBNetworkDevice &device, SBMacAddress mac){ void SBNetwork::initializeNetworkDevice(SBNetworkDevice &device, SBMacAddress mac){
Serial.print(F("Try to read device config from internal flash...")); Serial.print(F("Try to read device config from internal flash..."));
#if defined(ESP8266) #if defined(ESP8266)
EEPROM.begin(FLASH_SIZE); EEPROM.begin(SB_NETWORK_FLASH_SIZE);
#endif #endif
EEPROM.get(0, device); // The first two bytes of a storage must always be 'D' 'S' ID to identifiy, that the device was already initiated EEPROM.get(0, device); // The first two bytes of a storage must always be 'D' 'S' ID to identifiy, that the device was already initiated
if (device.ID[0] == 'D' && device.ID[1] == 'S'){ if (device.ID[0] == 'D' && device.ID[1] == 'S'){
@ -121,9 +121,9 @@ void SBNetwork::initializeNetworkDevice(SBNetworkDevice &device, SBMacAddress ma
void SBNetwork::resetData(){ void SBNetwork::resetData(){
Serial.print(F("Erasing device configuration data...")); Serial.print(F("Erasing device configuration data..."));
#if defined(ESP8266) #if defined(ESP8266)
EEPROM.begin(FLASH_SIZE); EEPROM.begin(SB_NETWORK_FLASH_SIZE);
#endif #endif
for(uint16_t i = 0; i < FLASH_SIZE; i++){ for(uint16_t i = 0; i < SB_NETWORK_FLASH_SIZE; i++){
EEPROM.write(i, 0); EEPROM.write(i, 0);
} }
#if defined(ESP8266) #if defined(ESP8266)
@ -147,7 +147,6 @@ bool SBNetwork::sendToDevice(SBMacAddress mac, void* message, uint8_t messageSiz
frame.Header = header; frame.Header = header;
uint8_t maxPackageSize = MAX_PACKAGE_SIZE; uint8_t maxPackageSize = MAX_PACKAGE_SIZE;
if (messageSize <= maxPackageSize){ if (messageSize <= maxPackageSize){
//Serial.print(" with no fragmentation");
// We can send directly without fragmentation // We can send directly without fragmentation
frame.Header.FragmentNr = 0; frame.Header.FragmentNr = 0;
frame.Header.FragmentCount = 1; frame.Header.FragmentCount = 1;
@ -166,15 +165,11 @@ bool SBNetwork::sendToDevice(SBMacAddress mac, void* message, uint8_t messageSiz
return bSuccess; return bSuccess;
} }
else{ else{
//Serial.print(" with fragmentation ");
// We have to send it in fragments // We have to send it in fragments
uint8_t fragmentCount = messageSize / maxPackageSize; uint8_t fragmentCount = messageSize / maxPackageSize;
if ((fragmentCount * maxPackageSize) < messageSize){ if ((fragmentCount * maxPackageSize) < messageSize){
fragmentCount++; fragmentCount++;
} }
//Serial.println(F("Have to send fragments"));
//Serial.print(F("Fragment count = "));
//Serial.println(fragmentCount, DEC);
for (uint8_t i = 0; i < fragmentCount; i++){ for (uint8_t i = 0; i < fragmentCount; i++){
#if defined(_DEBUG) #if defined(_DEBUG)
Serial.print("."); Serial.print(".");
@ -196,7 +191,7 @@ bool SBNetwork::sendToDevice(SBMacAddress mac, void* message, uint8_t messageSiz
bool bSuccess = this->sendToDevice(frame); bool bSuccess = this->sendToDevice(frame);
if (!bSuccess){ if (!bSuccess){
#if defined(_DEBUG) #if defined(_DEBUG)
Serial.println(" Failed"); Serial.println(" Failed ");
#endif #endif
return false; return false;
} }
@ -208,23 +203,74 @@ bool SBNetwork::sendToDevice(SBMacAddress mac, void* message, uint8_t messageSiz
} }
} }
bool SBNetwork::sendToDevice(SBNetworkFrame frame){ bool SBNetwork::sendToDeviceInternal(SBNetworkFrame frame, bool waitForAck) {
uint8_t bufferSize = sizeof(SBNetworkHeader) + frame.MessageSize; uint8_t bufferSize = sizeof(SBNetworkHeader) + frame.MessageSize;
uint8_t buffer[32]; // = (uint8_t*)malloc(bufferSize); uint8_t buffer[32]; // = (uint8_t*)malloc(bufferSize);
memcpy(buffer, &frame.Header, sizeof(SBNetworkHeader)); memcpy(buffer, &frame.Header, sizeof(SBNetworkHeader));
if (frame.MessageSize > 0){ if (frame.MessageSize > 0) {
memcpy(buffer + sizeof(SBNetworkHeader), frame.Message, frame.MessageSize); memcpy(buffer + sizeof(SBNetworkHeader), frame.Message, frame.MessageSize);
} }
// Send to broadcast bool bSuccess = false;
radio.stopListening(); uint8_t iCounter = 0;
radio.openWritingPipe(frame.Header.ToAddress); while (!bSuccess && iCounter < RETRY_COUNT) {
bool bSuccess = radio.write(buffer, bufferSize); // Send to broadcast
//free(buffer); radio.stopListening();
radio.openReadingPipe(0, this->NetworkDevice.MAC); radio.openWritingPipe(frame.Header.ToAddress);
radio.startListening(); bSuccess = radio.write(buffer, bufferSize);
radio.openReadingPipe(0, this->NetworkDevice.MAC);
radio.startListening();
if (bSuccess) {
bSuccess = waitForAck ? waitForAckFrom(frame.Header.ToAddress) : true;
}
delay(40); // Waittime between two sendings
iCounter++;
}
return bSuccess; return bSuccess;
} }
bool SBNetwork::sendToDevice(SBNetworkFrame frame){
return sendToDeviceInternal(frame, true);
}
bool SBNetwork::waitForAckFrom(SBMacAddress mac) {
#if defined(_DEBUG)
Serial.print(F("Wait for Ack... "));
#endif
long lNow = millis();
// We need the counter in case of the unoccationally case, that during the wait, the timer overflows ans starts with 0
uint16_t iCounter = 1;
SBNetworkFrame frame;
while (lNow + ACK_WAIT > millis() && iCounter) {
if (this->receiveInternal(&frame)) {
if (frame.Header.FromAddress.isEquals(mac)) {
if (frame.Header.CommandType == SB_COMMAND_ACK) {
#if defined(_DEBUG)
Serial.print(F("Done - Counter = ")); Serial.println(iCounter);
#endif
return true;
}
}
}
iCounter++;
}
#if defined(_DEBUG)
Serial.print(F("Failed - Counter = ")); Serial.println(iCounter);
#endif
return false;
}
bool SBNetwork::sendAckTo(SBMacAddress mac) {
SBNetworkFrame frame;
frame.Header.ToAddress = mac;
frame.Header.FromAddress = this->NetworkDevice.MAC;
frame.Header.FragmentCount = 1;
frame.Header.FragmentNr = 0;
frame.Header.PackageId = millis();
frame.Header.CommandType = SB_COMMAND_ACK;
frame.MessageSize = 0;
return sendToDeviceInternal(frame, false);
}
bool SBNetwork::receiveInternal(SBNetworkFrame *frame) { bool SBNetwork::receiveInternal(SBNetworkFrame *frame) {
uint8_t pipe = -1; uint8_t pipe = -1;
if (radio.available(&pipe)) { if (radio.available(&pipe)) {
@ -278,20 +324,17 @@ bool SBNetwork::receiveMessage(void **message, uint8_t *messageSize, SBMacAddres
return true; return true;
} }
else if (frame.Header.FragmentNr == 0) { else if (frame.Header.FragmentNr == 0) {
//Serial.print(frame.Header.FragmentNr + 1); Serial.print("/"); Serial.println(frame.Header.FragmentCount);
// We have to receive more packages // We have to receive more packages
//uint8_t *buffer = (uint8_t*)malloc((frame.MessageSize * frame.Header.FragmentCount));
memcpy(_ReadBuffer, frame.Message, maxPackageSize); memcpy(_ReadBuffer, frame.Message, maxPackageSize);
//free(frame.Message); delay(50); // We need a delay here, because the opposite needs time to send the next package
delay(10);
while (radio.available()) { while (radio.available()) {
bReceive = this->receive(&frame); bReceive = this->receive(&frame);
if (!bReceive) { if (!bReceive) {
//free(buffer);
return false; return false;
} }
else { else {
memcpy(_ReadBuffer + (frame.Header.FragmentNr * maxPackageSize), frame.Message, frame.MessageSize); memcpy(_ReadBuffer + (frame.Header.FragmentNr * maxPackageSize), frame.Message, frame.MessageSize);
//free(frame.Message);
if (frame.Header.FragmentNr == (frame.Header.FragmentCount - 1)) { if (frame.Header.FragmentNr == (frame.Header.FragmentCount - 1)) {
// Last fragment received // Last fragment received
*message = _ReadBuffer; *message = _ReadBuffer;
@ -302,14 +345,9 @@ bool SBNetwork::receiveMessage(void **message, uint8_t *messageSize, SBMacAddres
delay(10); delay(10);
} }
} }
//free(buffer);
return false; return false;
} }
else { else {
if (frame.Message != NULL) {
//free(frame.Message);
}
return false; return false;
} }
} }
@ -339,7 +377,7 @@ bool SBNetwork::connectToNetwork(){
frame.Header = header; frame.Header = header;
frame.Message = NULL; frame.Message = NULL;
frame.MessageSize = 0; frame.MessageSize = 0;
bool bMasterAck = this->sendToDevice(frame); bool bMasterAck = this->sendToDeviceInternal(frame, false);
unsigned long started_waiting_at = millis(); unsigned long started_waiting_at = millis();
boolean timeout = false; boolean timeout = false;
while (!this->receive(&frame)) { while (!this->receive(&frame)) {
@ -375,7 +413,7 @@ bool SBNetwork::connectToNetwork(){
conFrame.Header.PackageId = millis(); conFrame.Header.PackageId = millis();
conFrame.Header.ToAddress = frame.Header.FromAddress; conFrame.Header.ToAddress = frame.Header.FromAddress;
conFrame.MessageSize = 0; conFrame.MessageSize = 0;
if (!this->sendToDevice(conFrame)) { if (!this->sendToDeviceInternal(conFrame, false)) {
Serial.println(F("Failed - Sending pairing request")); Serial.println(F("Failed - Sending pairing request"));
} }
else { else {
@ -415,7 +453,7 @@ bool SBNetwork::connectToNetwork(){
} }
} }
bool SBNetwork::pingDeviceInternal(SBMacAddress mac, bool waitForAck) { bool SBNetwork::pingDevice(SBMacAddress mac){
SBNetworkHeader header; SBNetworkHeader header;
header.ToAddress = mac; header.ToAddress = mac;
header.FromAddress = this->NetworkDevice.MAC; header.FromAddress = this->NetworkDevice.MAC;
@ -431,35 +469,14 @@ bool SBNetwork::pingDeviceInternal(SBMacAddress mac, bool waitForAck) {
bool bSend = this->sendToDevice(frame); bool bSend = this->sendToDevice(frame);
if (bSend) { if (bSend) {
if (waitForAck) { Serial.println(F("Done - Device available"));
long lNow = millis();
SBNetworkFrame pingAckFrame;
while ((lNow + 100) > millis()) {
if (this->receiveInternal(&pingAckFrame)) {
if (pingAckFrame.Header.CommandType == SB_COMMAND_PING) {
Serial.println(F("Done - Device available"));
return true;
}
}
}
#if defined(_DEBUG)
Serial.println(F("Failed - Device not responding"));
#endif
return false;
}
} }
#if defined(_DEBUG)
else { else {
Serial.println("Failed - Device not responding"); Serial.println("Failed - Device not responding");
} }
#endif
return bSend; return bSend;
} }
bool SBNetwork::pingDevice(SBMacAddress mac){
return pingDeviceInternal(mac, true);
}
bool SBNetwork::handleCommandPackage(SBNetworkFrame *frame){ bool SBNetwork::handleCommandPackage(SBNetworkFrame *frame){
if (!this->RunAsClient) { if (!this->RunAsClient) {
// First check, if the device is listed in the storage // First check, if the device is listed in the storage
@ -472,81 +489,74 @@ bool SBNetwork::handleCommandPackage(SBNetworkFrame *frame){
} }
} }
if (!bFound) { // Look, if we must handle a command package
// If an unknown device was detected, then never handle the network control traffic and never handle the messages
#ifdef _DEBUG
Serial.print(F("Unknown device detected with MAC: "));
printAddress(frame->Header.FromAddress);
Serial.println();
#endif
//return false;
}
switch (frame->Header.CommandType) { switch (frame->Header.CommandType) {
case SB_COMMAND_PING: { case SB_COMMAND_PING: {
#ifdef _DEBUG #ifdef _DEBUG
Serial.println(F("Received 'PING'")); Serial.println(F("Received 'PING'"));
#endif #endif
// Only, when the device is a paired slave, send a ping back
if (bFound) { if (bFound) {
pingDeviceInternal(frame->Header.FromAddress, false); sendAckTo(frame->Header.FromAddress);
} }
break; break;
} }
case SB_COMMAND_SEARCH_MASTER: { case SB_COMMAND_SEARCH_MASTER: {
#ifdef _DEBUG Serial.print(F("Received 'SEARCH_MASTER'. "));
Serial.print(F("Received 'SEARCH_MASTER' Package. ")); // When automatic Client adding is activated
#endif
if (_EnableAutomaticClientAdding) { if (_EnableAutomaticClientAdding) {
#ifdef _DEBUG Serial.print(F("Send MasterACK..."));
Serial.println(F("Send MasterACK..."));
#endif
delay(20); delay(20);
bool bSend = sendMasterAck(frame->Header.FromAddress); bool bSend = sendMasterAck(frame->Header.FromAddress);
if (bSend) { if (bSend) {
return false; Serial.println(F("Done"));
}
else {
Serial.println(F("Failed"));
} }
Serial.println(F("Done"));
} }
#if defined(_DEBUG)
else { else {
Serial.println(F("AutomaticClientAdding is deactivaed. Ignoring package.")); Serial.println(F("AutomaticClientAdding is deactivaed. Ignoring package."));
} }
#endif
break; break;
} }
case SB_COMMAND_REQUEST_PAIRING: { case SB_COMMAND_REQUEST_PAIRING: {
#ifdef _DEBUG Serial.print(F("Received 'PAIRING_REQUEST'. "));
Serial.print(F("Received 'PAIRING_REQUEST' Package. ")); // When automatic Client adding is activated
#endif
if (_EnableAutomaticClientAdding) { if (_EnableAutomaticClientAdding) {
#ifdef _DEBUG Serial.print(F("Send PairingACK... "));
Serial.println(F("Send MasterACK..."));
#endif
delay(20); delay(20);
// This is the point where we could stop orpcessing and wait for an user input on the controller to let the new device access the network // This is the point where we could stop prpcessing and wait for an user input on the controller to let the new device access the network
bool bSend = sendPairingAck(frame->Header.FromAddress); bool bSend = sendPairingAck(frame->Header.FromAddress);
// If sending was successfull, then add the new slave
if (bSend) { if (bSend) {
Serial.println(F("Done"));
Serial.print(F("Storing new MAC to MasterStorage... "));
addMac(frame->Header.FromAddress); addMac(frame->Header.FromAddress);
Serial.println(F("Done"));
}
else {
Serial.println(F("Failed"));
} }
} }
#if defined(_DEBUG)
else { else {
Serial.println(F("AutomaticClientAdding is deactivaed. Ignoring package.")); Serial.println(F("AutomaticClientAdding is deactivaed. Ignoring package."));
} }
#endif
break; break;
} }
case SB_COMMAND_NO_COMMAND: case SB_COMMAND_NO_COMMAND:
default: { #ifdef _DEBUG
//Serial.println("No Command received. Passing through transport layer."); Serial.println(F("Received 'NO_COMMAND'"));
return bFound; #endif
break; if (bFound) {
} return sendAckTo(frame->Header.FromAddress);
}
} }
// Package was handled by handleCommandPackage(); // Package was handled by handleCommandPackage();
return false; return false;
} }
else { else {
return true; return sendAckTo(frame->Header.FromAddress);
} }
} }
@ -563,7 +573,7 @@ bool SBNetwork::sendMasterAck(SBMacAddress mac){
frame.Header = header; frame.Header = header;
frame.Message = (uint8_t*)&(this->NetworkDevice.NetworkKey); frame.Message = (uint8_t*)&(this->NetworkDevice.NetworkKey);
frame.MessageSize = sizeof(uint32_t); frame.MessageSize = sizeof(uint32_t);
return this->sendToDevice(frame); return this->sendToDeviceInternal(frame, false);
} }
else { else {
return false; return false;
@ -584,7 +594,7 @@ bool SBNetwork::sendPairingAck(SBMacAddress mac){
frame.Header = header; frame.Header = header;
frame.Message = NULL; frame.Message = NULL;
frame.MessageSize = 0; frame.MessageSize = 0;
return this->sendToDevice(frame); return this->sendToDeviceInternal(frame, false);
} }
else { else {
return false; return false;

View File

@ -65,6 +65,8 @@ class SBNetwork{
void initializeNetworkDevice(SBNetworkDevice &device, SBMacAddress mac); void initializeNetworkDevice(SBNetworkDevice &device, SBMacAddress mac);
bool sendToDeviceInternal(SBNetworkFrame frame, bool waitForAck);
bool sendToDevice(SBNetworkFrame frame); bool sendToDevice(SBNetworkFrame frame);
// Return false, if the package was handled internally and if it is not relevant for the end user // Return false, if the package was handled internally and if it is not relevant for the end user
@ -80,7 +82,9 @@ class SBNetwork{
bool receiveMessage(void **message, uint8_t *messageSize, SBMacAddress *mac); bool receiveMessage(void **message, uint8_t *messageSize, SBMacAddress *mac);
bool pingDeviceInternal(SBMacAddress mac, bool waitForAck); bool waitForAckFrom(SBMacAddress mac);
bool sendAckTo(SBMacAddress mac);
public: public:
/* /*
@ -183,5 +187,13 @@ public:
return _EnableAutomaticClientAdding; return _EnableAutomaticClientAdding;
} }
/*
Returns the count of bytes that are reserved for SBNetworkLib in the internal flash.
If you want to use the internal flash, get shure not writing into the storage of SBNetwork.
*/
uint16_t getFlashOffset() {
return SB_NETWORK_FLASH_SIZE;
}
}; };
#endif #endif

View File

@ -2,19 +2,23 @@
#ifndef _SB_NETWORK_CONFIG_ #ifndef _SB_NETWORK_CONFIG_
#define _SB_NETWORK_CONFIG_ #define _SB_NETWORK_CONFIG_
// Uncomment the following line, to compile the library for a master device. // Generates details debug messages about sending and receiving data packages
//#define RUN_AS_MASTER
//#define _DEBUG //#define _DEBUG
#define MASTER_CHECK_INTERVAL 0 // All slaves will ping the master every xxx milliseconds. if set to 0, they will not ping the master // All slaves will ping the master every xxx milliseconds. if set to 0, they will not ping the master
#define MASTER_CHECK_INTERVAL 0
#define MAX_CLIENTS 10 #define MAX_CLIENTS 10
#define CLIENT_TIMEOUT 20000 #define CLIENT_TIMEOUT 60000
#define FLASH_SIZE 512 // Waittime for an ACK package
#define ACK_WAIT 50
#define ACK_WAIT 100 // Count of trys to send a data package
#define RETRY_COUNT 10
// Milliseconds to wait between two retries
#define RETRY_DALY 40
#endif #endif

View File

@ -15,6 +15,8 @@
#define SB_COMMAND_REQUEST_PAIRING 4 #define SB_COMMAND_REQUEST_PAIRING 4
// Will be sent from the master after successfule adding a new client // Will be sent from the master after successfule adding a new client
#define SB_COMMAND_PAIRING_ACK 5 #define SB_COMMAND_PAIRING_ACK 5
// Will always be sent, when packages are received and the other device is a known device
#define SB_COMMAND_ACK 6
class SBMacAddress{ class SBMacAddress{
public: public: