preloader

Auf AEQ-WEB Suchen

AEQ-WEB | Blog

LoRaWAN ABP Frame Counter Problem

Das LoRaWAN Frame Counter Problem (ABP)

Der Frame Counter ist ein Sicherheitsfeature von LoRaWAN. Während bei OTTA der Frame Counter nach jedem JOIN zurückgesetzt wird, bleibt der Frame Counter bei ABP bestehen. Besonders bei Selbstbau-Nodes wie mit dem ESP32 LoRa Board oder einem Arduino Uno kommt es zu Problemen mit dem Frame Counter im ABP Betrieb. In diesem Artikel geht es um Lösungsmöglichkeiten, wie man den Frame Counter Stand auch nach einem Reset oder Stromausfall im ESP32 beibehalten kann.

Wie funktioniert der Frame Counter?

Der Frame Counter ist ein Zähler, der bei jeder Aussendung um den Wert 1 erhöht wird. Der aktuelle Wert wird sowohl im Endgerät als auch am LoRaWAN-Server gespeichert. Der Uplink-Framecounter zählt jene Pakete, welche vom Endgerät zum LoRaWAN-Netzwerk gesendet werden. Der Downlink-Framecounter zählt jene Pakete, welche vom LoRaWAN-Netzwerk zum Endgerät gesendet werden. Grundsätzlich ist es nicht möglich, ein Datenpaket zu entschlüsseln, wenn man nicht den App-Session-Key kennt. Trotzdem besteht die Möglichkeit, ein Datenpaket mit einem Empfänger Binär (über die Modulation) aufzuzeichnen und erneut abzusenden. Das LoRaWAN-Netzwerk kann nicht unterscheiden, ob das Datenpaket vom gleichen Absender kommt oder ob es von einem fremden System kopiert und erneut abgesendet wurde. Mit dem Frame Counter wird dies verhindert. Der Frame Counter wird direkt in die Verschlüsselung mit hinein gerechnet, wodurch physikalisch jedes Datenpaket anders aussieht. Ein erneutes Absenden eines aufgezeichneten Datenpakets ist somit nicht mehr möglich. Übrigens wird so ein Angriff auch als Replay-Attack bezeichnet.

Speichermöglichkeiten

Jeder Mikrocontroller und auch jedes Development Board hat verschiedene Speicher eingebaut. Grundsätzlich findet man auf jedem Mikrocontroller einen Arbeitsspeicher (RAM) und einen Programmspeicher (meist Flash). Darüber hinaus gibt es dann entweder eine Partition im Flash, wo Daten permanent während der Programmlaufzeit abgespeichert werden können oder es gibt einen EEPROM. Auf vielen Entwicklungs-Boards findet man oft auch externe Speicher wie einen SPI-Flash oder einen batteriegepufferten RAM.

ESP32 Permanent Speicher
Der ESP32 hat einen bestimmten Speicherbereich (meist etwa 20 KB) im internen Flash reserviert, der genau für solche Zwecke vorgesehen ist.

Arduino Permanent Speicher
Der Arduino hat zwar auch einen Flash Speicher, aber auf diesen Speicher greift in der Regel nur der Bootloader zu. Während der Programmlaufzeit ist ein Zugriff nur bedingt und sehr aufwendig möglich. Aber es gibt einen integrierten EEPROM, der je nach Prozessortyp von 512 Byte bis zu zwei Kilobyte groß ist. Auch dieser Speicher ist für den Frame Counter geeignet. In diesem Artikel wird jedoch nur auf den ESP32 eingegangen, aber der Speicherzugriff am Arduino funktioniert ähnlich. Weitere Information zum Arduino EEPROM Zugriff gibt es in diesem Artikel.

Die Software

Der folgende Beispielcode zeigt den Schreib- und Lesezugriff auf den internen Flash. Als Bibliothek wird die "Preferences Library" verwendet. Diese Library wird mit dem ESP32 im Arduino IDE bereits automatisch vorinstalliert.

//More information at: https://www.aeq-web.com

#include <Preferences.h>

Preferences preferences;

void setup() {
  delay(2000);
  Serial.begin(115200);
  Serial.println();
  Serial.print("Counter after Reboot: ");
  Serial.println(fcnt_read());
}

void loop() {
  delay(10000);
  fcnt_up();
  Serial.print("Counter: ");
  Serial.println(fcnt_read());
}

unsigned int fcnt_read() {
  preferences.begin("fcnt", true);
  unsigned int counter = preferences.getUInt("counter", 0);
  preferences.end();
  return counter;
}

void fcnt_up() {
  preferences.begin("fcnt", false);
  unsigned int counter = preferences.getUInt("counter", 0);
  preferences.putUInt("counter", counter+1);
  preferences.end();
}

Im Setup wird über die Funktion "fcnt_read()" der letzte Frame Counter aufgerufen und im Serial Monitor ausgegeben. Die Integer-Funktion "fcnt_read" startet mit "preferences.beginn" einen Speicherzugriff auf den Bereich "fcnt". Das "true" definiert einen Read-Only Zugriff. Anschließend wird ein neues Interger mit den Variablennamen "counter" deklariert. Über "getUInit" wird die Variable "counter" aus dem Speicher gelesen. Die Null am Ende dieser Zeile wird ausgegeben, wenn die Variable im zuvor definierten Speicherbereich nicht gefunden wurde. In der Praxis startet somit der Frame Counter auch erstmalig bei 0. Über das return wird der aktuelle Zählerstand von der Funktion zurück gegeben.

Die Funktion "fcnt_up" ruft zuerst den aktuellen Zählerstand (gleich wie bei "fcnt_read()") vom Speicher ab. Mit "putUInt" wird der neue Zählerstand in den Speicher geschrieben. Mit "counter+1" wird der Zähler vor dem Speichern um den Wert 1 erhöht.

Im Loop wird alle 10 Sekunden der Zählerstand mit "fcnt_up" um den Wert 1 erhöht. Anschließend wird aus dem Speicher der Zählerstand ausgelesen und über den Serial Monitor ausgegeben. Der Zählerstand bleibt somit auch nach einem Reset oder Programmupload erhalten.

Limit an Schreibvorgänge

Jeder Speicher hat eine begrenzte Anzahl an Schreibzyklen. Egal ob beim ESP32 der Flash Speicher oder am Arduino der EEPROM, man kann hier mit etwa 100.000 Schreibzyklen rechnen. Aus der Praxis ist jedoch bekannt, dass die maximale Anzahl an Schreibzyklen deutlich höher ist. Es ist keine Seltenheit, dass zum Beispiel erst nach 400.000 Zyklen beim Arduino EEPROM erstmalig ein Speicherfehler auftritt. Lesezugriffe können dagegen beliebig oft durchgeführt werden.

Die folgende Tabelle zeigt die zu erwartende Lebenszeit des Speichers auf Basis von 100.000 Schreibzyklen:

Intervall Lebenszeit
Jede Minute ~ 70 Tage
Alle 5 Minuten ~ 347 Tage
Alle 15 Minuten ~ 2,8 Jahre
Jede Stunde ~ 11,4 Jahre


Tipp: Schreibvorgänge optimieren

Um den jeweiligen Speicher zu schonen, kann man auch den Frame Counter in bestimmten Intervallen speichern. Hat man eine Wetterstation, die alle 10 Minuten neue Daten sendet, so könnte man den Frame Counter auch nur jede Stunde in den Speicher schreiben. Kommt es zu einem Stromausfall und der Zählerstand ist mit dem LoRaWAN Netzwerk nicht synchron abgespeichert, so ist damit zu rechnen, dass die nächsten Datenpakete abgelehnt werden. Sobald der Zähler wieder höher ist als jener vom vorherigen gültigen Datenpaket, nimmt das LoRaWAN Netzwerk diese Datenpakete wieder an. Man kann also den Speicher schonen, muss aber dafür kurze Ausfallzeiten nach einem Stromausfall bei der Hardware in Kauf nehmen. Alternativ bleibt nur die Möglichkeit, auf OTTA umzusteigen.

Der vollständige Code

Der vollständige Quellcode basiert auf der LMIC Library mit angepassten Beispielcode für die permanente Speicherung des Framecounters. Der vollständige Beispielcode und alle weiteren Beispiele befinden sich auf dieser Seite.


Share:
thumbnail
07.09.2019
ESP32 LoRa Arduino IDE Setup

In diesem Artikel zeigen wir, wie ein ESP32 LoRa Board im Arduino IDE konfiguriert wird und wie der angepasste Beispielcode für LoRa funktioniert

Alex @ AEQ-WEB
thumbnail
03.01.2021
Arduino & ESP32 bei TTN registrieren (ABP)

Diese Seite beschreibt die Registrierung und Konfiguration eines LoRa Development Boards wie Arduino, ESP32, TTGO und Ähnliches als Device bei TTN

Alex @ AEQ-WEB