Über unsMediaKontaktImpressum
Prof. Dr. Thomas Scheffler 05. Mai 2015

RESTful Services für 6LoWPAN Networks

Die Verknüpfung netzwerkfähiger Sensoren und Aktoren zum Internet der Dinge ist momentan ein wichtiger Trend, der die Forschung und Entwicklung im Netzwerkbereich beeinflusst. Die fortschreitende Vernetzung von elektrisch steuerbaren Geräten ermöglicht neuartige Nutzungs- und Interaktionsszenarien. Die Entwicklung von Softwareanwendungen für diese Anwendungen sollte auf etablierte Entwurfsmuster verteilter Anwendungen zurückgreifen. Dabei muss allerdings beachtet werden, dass aufgrund der stark eingeschränkten Ressourcen der Geräte eine verstärkte Integration und Anpassung der Softwarekomponenten nötig ist.

Die Vernetzung intelligenter Geräte, so genannter Smart Objects, bringt einige neue Herausforderungen mit sich. Anders als bei Computern und Smartphones stellt die Netzwerkfähigkeit keine Hauptanforderung dar, sondern wird als Zusatznutzen zur eigentlichen Gerätefunktion implementiert. Die Produktlebenszyklen, Support- und Installationsanforderungen an solche Geräte sind teilweise sehr verschieden von bekannten Anwendungsszenarien für Netzwerktechnik. So sollen sich zum Beispiel Sensornetze mit tausenden von Sensoren automatisch konfigurieren, Netzwerkverkehr automatisch nach Kriterien der Energieeffizienz routen und im Batteriebetrieb mehrere Jahre einsatzfähig sein. Darüberhinaus sollen die einzelnen Geräte nur wenige Euro kosten.

Diese Anforderungen haben in der Vergangenheit dazu geführt, dass solche Netzwerke anwendungsspezifische Protokolle einsetzen, die einen reduzierten Funktionsumfang bereitstellen und eine sehr starke vertikale Integration aufweisen (Zigbee, Bluetooth). Die typischen Erfolgsmerkmale eines vollständigen Internet-Protokoll Stacks, wie globale Adressierbarkeit und Erreichbarkeit, Bereitstellung einer abstrakten Socket-Schnittstelle für die Netzwerkintegration von Anwendungen und der Einsatz unterschiedlicher Transportprotokolle lassen sich in solchen Netzen nur sehr eingeschränkt realisieren und setzen in der Regel den Einsatz von Gateways voraus.

Fortschritte auf dem Gebiet der Mikrocontroller, der Übertragungstechnik und des Netzprotokolldesigns haben es mittlerweile aber möglich gemacht, die oben genannten Anforderungen auf Basis kostengünstiger und energieeffizienter Mikrocontroller zu realisieren, welche über eine standardkonforme Implementierung des Internet-Protokolls verfügen. Als Softwarebasis für die Entwicklung eines netzwerkfähigen Geräts bietet sich zum Beispiel Contiki an [1]. Contiki ist ein lizenzfreies, minimales Betriebssystem, welches in C geschrieben ist und auf verschiedenen 8-, 16- und 32-bit-Mikrocontrollern, als auch nativ als Linux-Prozess lauffähig ist.

Der Einsatz eines Betriebssystems für leistungsschwache Mikrocontroller ist heutzutage noch nicht weit verbreitet, bietet aber eine Reihe von Vorzügen. So lassen sich Entwicklungszyklen verkürzen, da Anwendungen nicht direkt auf dem Mikrocontroller entwickelt und getestet werden müssen, der später zum Einsatz kommt. Die Entwicklergemeinde des Betriebssystems sorgt dafür, dass Basisfunktionen bereitgestellt werden und die Dokumentation aktuell gehalten wird.

Vernetzung auf Basis von IEEE 802.15.4

Internetfähige Geräte müssen über eine geeignete Netzwerkschicht verfügen um IP-Pakete transportieren zu können. Auch hier führen die erwähnten Anforderungen dazu, dass etablierte Protokolle und Netzzugriffsverfahren aus dem PC-Bereich ungeeignet sind, weil ihr Energiebedarf zu hoch ist oder bestimmte Netztopologien voraussetzt.

IEEE 802.15.4 ist ein Funkstandard für Home Networks, Industrie- und Gebäudeautomatisierung. Der Standard wurde Anfang der 2000er Jahre entworfen und hat das Ziel, kostengünstige, niedrig-bitratige Vernetzung für Geräte zu ermöglichen, die bisher nicht netzwerkfähig waren. Dafür musste insbesondere der Energiebedarf optimiert werden. Die Anforderungen an Datenrate und Latenz sind dagegen gering und sollten für einfache Kontroll- und Steuerungsfunktionen ausreichen. Gegenüber alternativen Technologien wie Bluetooth-LE bieten Netze auf Basis von IEEE 802.15.4 weiterhin den Vorteil der größeren Zahl adressierbarer Knoten (16-bit MAC-Adressen) und der möglichen Vollvermaschung (Mesh-Network). Knoten mit Routingfunktionalität können Nachrichten weiterleiten und mit wenigen Edge-Routern ein weitflächiges Netzwerk aufspannen. 

Der Standard definiert mehrere Übertragungsverfahren in verschiedenen lizenzfreien Übertragungsbändern:

  • 20 kbit/s bei 868 MHz
  • 40 kbit/s bei 915 MHz
  • 250 kbit/s bei 2.4 GHz (DSSS)

Die Netze können im Beacon-Modus betrieben werden. Dabei sendet ein Koordinator Beacon-Frames aus, welche der Synchronisation aller Netzknoten dienen. Inaktive Knoten können ihr Funkinterface für längere Zeit in den Ruhemodus versetzen ohne dabei Nachrichten zu verpassen. Der verwendete hybride TDMA-CSMA-Algorithmus kann weiterhin auch eine Kanalreservierung realisieren. Verbreiteter ist allerdings der Beacon-lose Modus. Hier kommt ein einfacher CSMA-CA-Algorithmus zum Einsatz, bei dem auf eine Synchronisierung der Knoten verzichtet wird.

IPv6

Implementierungen für das Internet der Dinge sollten sinnvollerweise direkt auf dem Internetprotokoll in Version 6 aufsetzen. Aufgrund des beschränkten Adressraums von IPv4 macht es keinen Sinn, ein Netzwerk mit mehreren Milliarden erwarteten Geräten mit 32-bit-Adressen zu konfigurieren [2].

Das IPv6-Kernprotokoll wurde 1998 von Steve Deering und Rob Hinden im RFC 2460 beschrieben und stellt durch 128-bit-(16 Byte)-Adressen eine nahezu unerschöpfliche Ressource dar. Gegenüber IPv4 weist es die Besonderheit auf, dass spezielle Protokollfunktionen in Erweiterungsheader ausgelagert wurden und eine stärker strukturierte Adressierung effizientes Routing auch innerhalb einer Site erlaubt.

IPv6-Endsysteme können sich im Netz automatisch konfigurieren, was für den vorliegenden Anwendungsfall von besonderem Interesse ist. Die bis vor wenigen Jahren noch mangelhafte Verbreitung von IPv6 stellt heute kein Hindernis mehr dar. Laut aktueller Statistik wurden Anfang 2015 in Deutschland bereits über 13% aller Anfragen an Google über IPv6 abgewickelt [3].

6LoWPAN

IEEE 802.15.4 definiert eine Framegröße von maximal 127 Byte. Je nach Adressierungsart und aktiven Sicherheitsfeatures können davon 93-118 Bytes als Payload genutzt werden. Die geringe Framegröße ergibt sich in direkter Konsequenz aus der niedrigen Datenrate. Größere Frames würden zu einer erhöhten Wahrscheinlichkeit führen, dass der Frame wegen eines Bitfehlers verworfen wird. Weiterhin minimieren kleinere Frames die Belegungszeit des Funkkanals. Hier ergibt sich ein Problem: Der IPv6-Standard fordert eine Path MTU von mindestens 1280 Bytes und benötigt bereits 40 Bytes für die Übertragung des einfachen IPv6-Headers. Für die Übertragung von IPv6-Paketen in IEEE 802.15.4 Frames wird daher eine Anpassungsschicht benötigt.

Der 6LoWPAN-Adaptation-Layer ist unter anderem in RFC 4919 [4] und RFC 4944 [5] standardisiert. 6LoWPAN definiert einen Mechanismus zu Link-Layer Fragmentierung/ Defragmentierung, ein Verfahren zur zustandslosen (stateless) Header Compression für den IP- und UDP-Header und unterstützt verschiedene Topologieoptionen.

Contiki

Contiki ist ein quelloffenes Betriebssystem für Embedded Devices. Es wurde ursprünglich am Swedish Institute of Computer Science (SICS) [6] entwickelt und verfügt über eine aktive internationale Entwicklergemeinde. Es werden verschiedene Hardwareplattformen unterstützt (z. B. Linux nativ, AVR, ARM, MSP430). Weiterhin ist es vergleichsweise einfach, eine Portierung selbst vorzunehmen, da Contiki komplett in C geschrieben ist.

Contiki bietet kooperatives Multitasking in Form sogenannter Protothreads. Es verfügt über einen IPv4/IPv6-Netzwerkstack und benötigt für minimale Betriebssystemfunktionen ca. 40 kB Flash und etwa 2 kB RAM. Je nach implementierter Anwendung fallen diese Anforderungen natürlich höher aus, sind aber immer noch auf einfachen Mikrocontrollern wie zum Beispiel einem ATmega 1281 realisierbar. Für den Einstieg in die Entwicklung mit Contiki biete es sich an, ein komplett konfiguriertes VM Image auf Basis von Ubuntu von der Webseite des Contiki-Projekts herunterzuladen.

REST (Representational State Transfer)

REST bezeichnet einen Architekturstil für verteilte Anwendungen und wird als leichtgewichtige Alternative zu Mechanismen wie RPC (Remote Procedure Calls) und Web Services (SOAP, WSDL, etc.) genutzt. Anwendungen die nach dem REST-Prinzip aufgebaut sind, werden auch als RESTful-Webservices bezeichnet. Sie benutzen URIs um Ressourcen zu adressieren, HTTP-Request-Methoden als einheitliches Interface und XML oder JSON (JavaScript Object Notation) für die Repräsentation der Daten.

Ressourcen sind das Schlüsselelement eines RESTful-Designs. Die Gesamtheit der Ressourcen in Kombination mit den Zugriffsmethoden definiert die REST API.

1. Ressourcen:

  • werden durch URIs identifiziert,
  • repräsentieren Zustände und Funktionalität einer verteilten Anwendung und
  • lassen sich aus allen Teilen des Systems unmittelbar ansprechen

2. Zugriffsmethoden:

REST nutzt für alle Operationen der Anwendung (Create, Read, Update, Delete) ausschließlich die standardisierten HTTP-Methoden (GET, POST, PUT, DELETE).

Es existieren keine benannten “Befehle” oder “Services” wie in RPC oder SOAP. Es ist in REST nicht notwendig, einen bestimmten Methodennamen zu kennen und aufzurufen (wie z. B. getProductPrice(x)). Stattdessen wird ein HTTP-Request auf eine bestimmte URI ausgeführt.

Bei der Anwendung ist zu beachten, dass insbesondere die HTTP PUT- und POST-Methoden semantische Unterschiede hinsichtlich ihrer Ausführung aufweisen. So ist PUT eine idempotente Methode. Durch die Anwendung von PUT wird eine Ressource erzeugt oder vollständig aktualisiert. Das bewirkt, dass die mehrfache Ausführung von PUT immer zum gleichen Ergebnis führt. Die POST-Methode hingegen ist nicht idempotent und erzeugt bei jedem Aufruf eine Child-Ressource oder fügt Daten zu einer existierenden Repräsentation hinzu.

Die folgenden Beispiele verdeutlichen die Anwendung der HTTP-Methoden:

  • GET: wird verwendet, um Daten oder Statusinformationen des Geräts zu erfragen.
  • PUT: kann die Gerätekonfiguration auf einen definierten Wert ändern.
  • POST: kann verwendet werden, um zwischen verschiedenen Optionen zyklisch zu wechseln.
  • DELETE: wird verwendet, um Zustandsinformationen, die in Form von Ressourcen auf Servern und Caches gespeichert sind, zu löschen.

Für die Implementierung eines RESTful-Services ist es sinnvoll, sich an die folgenden Designrichtlinien zu halten:

  • Client-Server-Architektur: RESTful-Services funktionieren nach dem Muster der Client-Server-Kommunikation. Client-Komponenten können Dienste für weitere Komponenten verfügbar machen und verhalten sich gegenüber anderen Clients damit selbst als Server.
  • Ressourcen-Web: Eine einzelne Ressource sollte nur relevante Daten bereitstellen, kann aber Links auf weitere Ressourcen enthalten (so wie im Web-Kontext allgemein üblich).
  • Zustandsverwaltung: Die Interaktion ist zustandslos, allerdings können Server und Ressourcen zustandsbehaftet sein. Um ein gute Skalierbarkeit zu gewährleisten, sollte jeder neue Request alle Daten für seine Ausführung beinhalten und nicht von früheren Interaktionen abhängig sein.
  • Caching: Ressourcen sollten cacheable sein (Angabe eines Ablaufdatums/-zeit). Dafür kann der HTTP-Cache-Control-Header benutzt werden.
  • Proxy Server: können Teil der Architektur sein, um Performance und Skalierbarkeit zu erhöhen.

Beispiel: Ein Service macht über folgende URL den Zugriff auf Berichte verschiedener Nutzer möglich: 
 example.com/reports/
über einen HTTP GET-Aufruf lassen sich die Berichte der einzelnen Nutzer aufrufen,
 example.com/reports/alice
oder alle vorhandenen Berichte abfragen: 
 example.com/reports/all

3. Repräsentation der Daten:

Anfragen werden als HTTP GET-Request an die spezifizierte Ressource (URI) gesendet:

RESTful-Services liefern als Antwort üblicherweise maschinenlesbare Daten und kein HTML. Dabei kommen XML-, JSON- und CSV-Formate zum Einsatz:

Wie erwähnt, werden RESTful-Services als Alternative zu Webservices und RPC eingesetzt. Vergleichbar mit Webservices, haben REST-Services folgende Eigenschaften:

  • Platform-independent (Server läuft unter Unix, Client unter Windows, ...),
  • Language-independent (C#-Server kommuniziert mit Java-Client, etc.),
  • basierend auf etablierten Standards (HTTP, XML, JSON),
  • Einsatz über Firewalls hinweg möglich.

REST ist kein Standard, sondern verwendet die Designprinzipien des WWW für den Datenaustausch zwischen Anwendungen. Verschiedene Frameworks [7] unterstützen und implementieren RESTful-Services und da es sich um ein sehr einfaches Konzept handelt, sind komplett eigene Implementierungen möglich. Für den praktischen Einsatz ist weiterhin zu bedenken, dass REST keine inhärenten Sicherheitsfeatures wie Verschlüsselung oder Session-Management bietet. Nutzer-Authentifizierung durch Benutzername-/Passwort-Token sowie die Nutzdatenverschlüsselung per Transport Layer Security (TLS) ist möglich, muss aber separat implementiert werden.

Anwendungsentwicklung unter Contiki OS

Für unser Ziel, der Entwicklung einer Anwendung für eine Mikrocontroller-Plattform, müssen wir uns kurz mit den Eigenarten der Software-Entwicklung vertraut machen. Aufgrund der sehr eingeschränkten Ressourcen eines Mikrocontrollers findet die Entwicklung nicht direkt auf dem Zielsystem statt. Der Quellcode wird mit Hilfe eines Cross-Compilers auf dem Entwicklungssystem übersetzt und anschließend über ein Programmiergerät auf das Zielsystem geflasht. Einfache Mikrocontroller verfügen weder über eine Speicherverwaltungseinheit noch über ein Dateisystem. Daher werden Anwendung, Betriebssysteme und Treiber in der Regel zu einem einzigen Binärobjekt übersetzt. All diese Komponenten stehen üblicherweise als Quellcode-Dateien zur Verfügung.

Der Contiki Quellcode hat eine feste Ordnerstruktur:

  • / ->  Im Contiki-Wurzelverzeichnis befindet sich das globale Makefile Makefile.include.
  • apps -> Hier befinden sich alle von Contiki bereitgestellten Applikationen. Diese sind nicht standardmäßig aktiviert. Der Quellcode benötigter Apps kann über das Makefile mit der Definition APPS=[Name des App-Ordners] hinzugefügt werden.
  • core -> In diesem Ordner befindet sich das eigentliche Betriebssystem, u. a. auch der Ordner net, der den kompletten Quellcode für den Netzwerkstack enthält. Im Ordner sys befindet sich der Quellcode für das Prozessmanagement und die Timer.
  • cpu -> Hier findet sich der Quellcode für die unterstützenden Prozessoren, also z. B. der Ordner avr für ATmega-Prozessoren oder der Ordner arm für ARM-Prozessoren. In den jeweiligen Unterordnern sind die Treiber für die unterstützte Peripherie zu finden.
  • doc -> Enthält die Dokumentation sowie Beispielcode für und über Contiki.
  • examples -> Dieser Ordner enthält fertige Contiki-Projekte. Er wird typischerweise zum Erstellen neuer Projekte genutzt. Existierende Projekte lassen sich nachnutzen.
  • platform -> Enthält alle spezifischen Einstellungen für die verwendete Hardware-Plattform und das verwendete Entwicklungsboard.
  • tools -> Sonstige Werkzeuge, die bei der Arbeit mit Contiki hilfreich sind, z. B. der Simulator cooja oder Quellcode für den SLIP-Border Router auf der PC-Seite.

Für die Anwendungsentwicklung unter Contiki steht ein ausgeklügeltes System von Makefiles bereit, welches die Details der plattformspezifischen Eigenheiten verbirgt. Es muss daher nur ein einfaches, projektspezifisches Makefile erzeugt werden. Das Makefile des "hello-world"-Beispiels (~/contiki/examples/hello-world/) sieht folgendermaßen aus:

CONTIKI_PROJECT = hello-world
all: $(CONTIKI_PROJECT)
CONTIKI = ../..
include $(CONTIKI)/Makefile.include

Zuerst wird der Name der Anwendung spezifiziert. Die CONTIKI-Variable verweist auf das Wurzelverzeichnis des Contiki-Quellcodes. Zum Schluss wird das systemweite Makefile Makefile.include eingebunden.

Durch die Angabe eines Targets für make kann die Anwendung für verschiedene Hardware-Plattformen übersetzt und bereitgestellt werden. Das Target minimal-net erlaubt dabei die Ausführung der Anwendung direkt als Prozess der Entwicklungsumgebung. Dadurch kann die Entwicklung beschleunigt und auf den Einsatz von Software-Debuggern zurückgegriffen werden. Wenn die Anwendung fehlerfrei läuft, kann diese dann einfach für die Zielplattform neu übersetzt werden:

  make TARGET=avr-raven

RESTful-Webserver

Das Contiki-Projekt stellt bereits einen REST HTTP-Server zur Verfügung, mit dessen Hilfe wir auf einfachem Weg einen eigenen REST-Service erstellen können. Dazu reicht es am Anfang, die Datei rest-server-example.c im Verzeichnis ~/contiki/examples/rest-example entsprechend anzupassen.

Über das RESOURCE-Makro wird der Name der Ressource, die HTTP-Zugriffsmethode und die URL definiert. Wollen wir zum Beispiel vom REST-Server eine Zufallszahl über den Aufruf der URL /rand abfragen, so wird die Ressource wie folgt definiert:

   RESOURCE(rand, METHOD_GET, "rand");

Jede Ressource benötigt weiterhin eine Handler-Funktion, welche die entsprechenden Daten erzeugt oder verarbeitet. Der Name der Handler-Funktion wird nach dem Muster [resource name]_handler gebildet.

Die nachfolgend dargestellte Funktion erzeugt beispielhaft ein einfaches JSON-Objekt, welches eine Zufallszahl zwischen 0 und 10 enthält und diese im char-Array temp abspeichert. Anschließend wird der Content-Typ des HTTP-Response-Headers gesetzt und die Payload generiert:

void 
rand_handler(REQUEST* request, RESPONSE* response)
{
     /*Construct a JSON Object containing random numbers*/
    sprintf(temp,"{\n\"test\" : \"%d\"\n}", rand() % 11);

     /*Set the content response header to JSON*/
     rest_set_header_content_type(response, APPLICATION_JSON);
    rest_set_response_payload(response, (uint8_t*)temp,
     strlen(temp));
}

Als letzte Aufgabe muss die neu erzeugte Ressource noch im Server-Thread bekanntgegeben werden. Der entsprechende Funktionsaufruf ist im Beispiel dargestellt: 

PROCESS_THREAD(rest_server_example, ev, data)
{
     ...  
     rest_activate_resource(&resource_rand);
}

Das passende Makefile für unser Beispiel sieht folgendermaßen aus:

all: rest-server-example  
CONTIKI=../..  
CFLAGS += -DPROJECT_CONF_H=\"project-conf.h\"
CFLAGS += -DWITH_HTTP
APPS += rest-http  
CONTIKI_WITH_IPV6 = 1

include $(CONTIKI)/Makefile.include


Anschließend kann das Projekt mit make TARGET=minimal-net übersetzt und mit ./ rest-server-example.minimal-net ausgeführt werden. Dabei fällt auf, dass der Contiki-Prozess unter Linux ein TAP-Interface erzeugt, um mit der Anwendung zu kommunizieren. Server und Interface verfügen erst einmal nur über link-lokale IPv6-Adressen und der Server erwartet Anfragen auf Port 8080.

Um unsere Anwendung einfacher testen zu können, sollten wir noch einige Änderungen am Quellcode vornehmen:

Dazu passen wir in der Datei ~/contiki/apps/rest-http/http-common.h die Werte der folgenden Makros an:

#define HTTP_PORT 80
#define HTTP_DATA_BUFF_SIZE 800

Weiterhin verwendet Contiki standardmäßig das Routing-Protokoll RPL [8], um das Netzwerkpräfix an die Netzknoten zu verteilen. Für den Test unserer Anwendung ist das allerdings eher hinderlich. Daher entfernen wir in der Datei ~/contiki/platform/minimal-net/contiki-conf.h den Kommentar vor dem Makro:

 #define HARD_CODED_ADDRESS      "fdfd::"

und fügen unmittelbar danach die folgenden beiden Zeilen ein:

 #undef UIP_CONF_IPV6_RPL   
#define UIP_CONF_IPV6_RPL       0

Anschließend können wir das Projekt erneut übersetzen und ausführen. Nachdem wir das TAP-Interface mit dem Befehl $ sudo ifconfig tap0 add fdfd::1/64 konfiguriert haben, ist unter der URL http:// [fdfd::206:98ff:fe00:232]/rand unser REST-Service erreichbar.

Die zuletzt beschriebenen Änderungen sind nur für die Ausführung als nativer Linux-Prozess relevant. Wenn Sie die Anwendung anschließend für eine passende Mikrocontroller-Plattform übersetzen, kommt standardmäßig wieder RPL zum Einsatz.

Für eine tiefergehende Beschäftigung mit dem Thema möchte ich Sie noch auf den "Workshop: RESTful Services mit Contiki" [9] verweisen.

Autor

Prof. Dr. Thomas Scheffler

Thomas Scheffler lehrt Datenkommunikation, Network Engineering und Network Security an der Beuth Hochschule für Technik in Berlin. Davor war er Senior Network Engineer im Bereich Packet-based Carrier Transport Networks der...
>> Weiterlesen
botMessage_toctoc_comments_9210