Kabelitzens - Der Blog

Szenen aus dem Leben

NAS: Wake on LAN

Wie ihr in einem anderen Post bereits mitbekommen habt, haben wir für unsere Fotos und Dokumente eine NAS (Network Attached Storage).  Da wir nicht ständig an unseren Computer sitzen, wollten wir nicht das Gerät 24/7 laufen lassen. Es sollte nur im aktiven Modus laufen, wenn wir tatsächlich an unseren Computern sind. Ansonsten wartet es auf uns und bleibt im Ruhemodus, der weniger Strom verbraucht. Wenn wir unsere Computer verlassen, soll die NAS die Arbeit wieder einstellen und erst beim nächsten Start eines Computers zur Stelle sein. Da ich hin und wieder gerne programmiere, wollte ich diese überschaubare Aufgabe selber in die Hand nehmen. Es gibt einige Webseiten, die fertige Binärdateien für einen Wake-On-Lan-Aufruf zur Verfügung stellen, aber das würde meine Learning-by-doing-Idee kaputt machen.

Was ist Wake on Lan?

Wake-on-Lan oder WOL ist ein Standard, der es erlaubt ausgeschaltete Computer über die Netzwerkkarte anzuschalten, wenn diese ein bestimmtes Byte-Muster empfängt. Dieses spezielle Byte-Muster heißt Magic Packet und besteht aus sechs Bytes mit dem hexadezimalem Wert FF und 16-mal die MAC-Adresse des Zielcomputers.

Da wir unser NAS im Heimnetzwerk haben und somit keine komplizierten Programmierungen brauchen, um z.B. über das Internet die NAS zu starten, konnte ich auf einem niedrigen Niveau ohne große Sicherheitsbedenken das kleine Programm entwerfen. Wer sich den Programmcode, inkl. Icon, in gänze ansehen will, findet ihn auf Github/WakeOnLan.

Meine Idee für die Umsetzung

Da ich eine möglichst einfache Anwendung haben will, die einmal kompiliert alle meine Bedürfnisse bedienen soll, ist die Umsetzung folgendermaßen: Das WOL-Programm soll im Autostart gestartet werden. Es sucht eine Datei, die die MAC-Adressen der Geräte enthält, die gestartet werden sollen. Es können also beliebige Geräte geweckt werden (so sie den WOL-Standard unterstützen). Darüber hinaus soll man die Geräte durch Kommentarzeilen auch benennen können und es soll egal sein, ob die MAC-Adresse mit Bindestrichen, Doppelpunkten oder ohne dergleichen geschrieben wird.
Auch die Datei, in der die MAC-Adressen stehen, sollte mindestens für Testzwecke einen beliebigen Namen tragen. Ebenso kann der Netzwerkport des zu weckenden Gerätes ausgewählt werden (wenn nichts gewählt wird, ist der Standardport gesetzt).

Implementierung für Windows

Ich bin ein Windows-Nutzer (meistens zu mindest). Daher habe ich eine Implementierung geschrieben, die für die Windows-Betriebssysteme ab Windows 7 funktionieren sollte.

Als ersten werden includes benötigt, um die Funktionalität des Windows-Sockets bereitzustellen:

#include WinSock2.h;
#include Windows.h;
#pragma comment(lib, "Ws2_32.lib")

Die Kernfunktionalität steht im folgenden Abschnitt. Die Funktionsargumente sind die MAC-Adresse des zu weckenden Gerätes, die Portadresse (default ist 9) und die Broadcast-Adresse (default is 0xFFFFFFFF, Broadcast Addresse im lokalen Netzwerk). Unter dem Code-Abschnitt gibt es noch ein paar kleine Erläuterungen.

bool SendWakeOnLAN(std::string MACAddress, unsigned PortAddress, unsigned long BroadcastAddress)
{

    // Build message = magic packet
    // 6x 0xFF followed 16x MACAddress
    unsigned char Message[102];
    for (auto i = 0; i < 6; ++i)
        Message[i] = 0xFF;

    unsigned char MAC[6];
    std::string StrMAC = MACAddress;
    RemoveCharsFromString(StrMAC, ":-");

    for (auto i = 0; i < 6; ++i)
    {
        MAC[i] = static_cast<unsigned char>(std::stoul(StrMAC.substr(i * 2, 2), nullptr, 16));
    }
 
    for (auto i = 1; i <= 16; ++i)
    {
        memcpy(&Message[i * 6], &MAC, 6 * sizeof(unsigned char));
    }
 
    // Create socket
    // Socket variables
    WSADATA WSAData;
    SOCKET SendingSocket = INVALID_SOCKET;
    struct sockaddr_in LANDestination {};

    // Initialize WinSock
    if (WSAStartup(MAKEWORD(2, 2), &WSAData) != NO_ERROR)
    {
        std::cout << "WSA startup failed with error:" << std::endl;
        std::cout << WSAGetLastError() << std::endl;
        return false;
    }
    else
    {
        // Initialize socket with protocol properties (internet protocol,     datagram-based protocol (size limited to 512bytes))
        SendingSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

        if (SendingSocket == INVALID_SOCKET)
        {
            std::cout << "Socket is not initialized:" << std::endl;
            std::cout << WSAGetLastError() << std::endl;
            return false;
        }

        // Set socket options (broadcast)
        const bool optval = TRUE;
        if (setsockopt(SendingSocket, SOL_SOCKET, SO_BROADCAST, (char*)&optval, sizeof(optval)) != NO_ERROR)
        {
            std::cout << "Socket startup failed with error:" << std::endl;
            std::cout << WSAGetLastError() << std::endl;
            return false;
        }

        LANDestination.sin_family = AF_INET;
        LANDestination.sin_port = htons(PortAddress);
        LANDestination.sin_addr.s_addr = BroadcastAddress;

        // Send Wake On LAN packet
        if (sendto(SendingSocket, (char*) Message, 102, 0,  reinterpret_cast<sockaddr*>(&LANDestination), sizeof(LANDestination)) ==  ERROR)
        {
            std::cout << "Sending magic packet fails with error:" << std::endl;
            std::cout << WSAGetLastError() << std::endl;
            return false;
        }

    }

    return true;
}

Zeilen 6-22: Das Magic Packet wird erstellt. Es ist 102 Bytes lang und enthält 6 Bytes mit 255 und 16 mal die Ziel-MAC-Adresse.

Zeile 12: Aufruf einer Funktionen, die aus den übergebenen MAC-Adressen die Bindestriche und Doppelpunkte entfernt.

Zeilen 26-28: WSADATA ist eine Windows Implementierung und enthält Informationen über die Socket Implementierung. In Zeile 31 wird die Version mittels der Funktion WSAStartup festgelegt. Für den SOCKET wird eine Art Nullpointer-Initialisierung gewählt. Die Socketeigenschaften werden später erstellt. Die Struktur sockaddr_in ist die IPv4-Implementierung, die festlegt, mit welcher Adresse sich der Socket verbindet (in unserem Fall ist es keine spezifische Adresse, sondern die Broadcast-Adresse).

Zeilen 33-35, 44-46, 53-55, 65-67: Diese Zeilen sind eher für Debuggingzwecke sinnvoll. Im Normalfall werden sie nicht aufgerufen. Wenn etwas schief laufen sollte, dann geben sie Hinweise, wo etwas nicht normal ist.

Zeile 40:  Hier werden die Socket-Eigenschaften festgelegt und der Socket initialisiert. AF_INET steht für IPv4 (da wir eine IPv4 Adresse broadcasten, der Socket-Type ist ein Datagramm des UDP, da wir die IPv4-Adresse des Gerätes nicht unbedingt kennen, und als Protokoll wird dann natürlich auch UDP festgelegt. Für mehr Informationen empfehle ich die SOCKET-Dokumentation.

Zeile 51 : Es werden die Socketoptionen gesetzt. die Argumente für den vorher initialisierten Socket sind: SOL_SOCKET Optionen erlauben das Setzen der Broadcast-Option mit SO_BROADCAST. Dieser Wert wird auf TRUE gesetzt, um das broadcasten zu ermöglichen.

Zeilen 58-60: Hier wird die sockaddr_in-Struktur mit Details gefüllt.  Die Protokolfamilie (IPv4), der Zielport, und die Broadcast-Adresse werden eingetragen. Mit der Funktion htons wird ein unsigned short in Netzwerk-Byteorder konvertiert.

Zeile 63: Die sendto-Funktion sendet schließlich unsere Message in das lokale Netzwerk über den vorbereiteten Socket. Es gibt einen Pointer auf unser Magic Packet, die Länge des Paketes in Byte, keine Flags (=0) werden gesetzt und mit LANDestination wird der gesetzte Port über den Broadcast angesprochen.

Um das Programm sauber zu beenden, wird geraten noch die Befehle

closesocket(SendingSocket);
WSACleanup();

aufzurufen, um den Socket zu schließen und den Socket abzumelden (und den DLL -Aufruf zu beenden). Diese beiden Befehle fehlen in dieser Auflistung, sind aber auf Github bereits eingefügt.

Wie funktioniert es?

Die ersten Tests sind gut ausgefallen. Das kompilierte Programm kann ich auf beiden Windows-Rechnern nutzen, die wir haben. Um es im Betrieb zu benutzen habe ich das Exe-File zusammen mit der MACAddressList.txt in einen Ordner kopiert. In das Textfile habe ich die MAC-Adresse des NAS eingetragen. Dann wird unter dem Ordner für Autostarts für alle User (Windows-Taste + R -> „shell:common startup“ aufrufen) eine Verknüpfung des Programms abgelegt.

Nun startet die NAS immer mit dem Start des Computers. Läuft die NAS schon, während ein Computer gestartet wird, passiert nichts weiter. Der Wake-On-Lan-Befehl wird ignoriert. Die NAS selber schaltet sich nach 30 Minuten ohne Verbindung wieder aus. Wenn also kein Rechner mehr zugreift wird die NAS wieder im Schlaf versinken.

Wie geht es weiter?

Beim Schreiben des Programms stand an aller erster Stelle der Spaß an der Sache. Da das Programm läuft, könnte ich mich nun um Features kümmern, wie z.B. das ganze auch für unsere Linux-Rechner erweitern oder das ganze etwas sicherer gestalten oder mit einer Benutzeroberfläche zu versehen, damit auch andere Rechner geweckt werden können ohne die Textdatei zu ändern. Aber ich bin erst einmal fertig mit diesem Projekt. Sogar ein Logo habe ich mir designed.

Falls du Interesse hast oder Fragen zum Programm, dann kannst du das gerne in den Kommentaren tun. Ich versuche zu helfen, wo ich kann, aber ich übernehme keinerlei Verantwortung für irgendwelche Probleme, die mit dem Code entstehen.

 

Zurück

September- und Oktoberbücher: Schwangerschaft und Kleinkinder

Nächster Beitrag

Was war. Was ist. Was wird. 2017

  1. Mama

    Hast du schon Nachfragen?
    Das ist ja der Wahnsinn!

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

fünfzehn + fünf =

* Die DSGVO-Checkbox ist ein Pflichtfeld

*

Ich stimme zu

Präsentiert von WordPress & Theme erstellt von Anders Norén