erkr
Published © GPL3+

Wake On LAN (WOL) Gateway

A safe way to wake WOL-enabled devices on your LAN remotely (via your router).

BeginnerProtip6,190
Wake On LAN (WOL) Gateway

Things used in this project

Hardware components

Wemos D1 Mini
Espressif Wemos D1 Mini
×1

Software apps and online services

Arduino IDE
Arduino IDE

Story

Read more

Code

WOL-Gateway.ino

C/C++
The whole WOL gateway
/*
   Wake On Lan Gateway for any ESP8266 based board. 
   Compiled on Arduine IDE version 1.8.3 and a Wemos D1 & Mini board
   Copyright: the GNU General Public License version 3 (GPL-3.0) by Eric Kreuwels, 2019

   Directly forwarding WOL UDP packets to a particular device on your network is in general not a good idea.  
   It potentially causes floading of data on your local LAN  induced by external sources
   It is also limited as it only allows wakeup one device per forwarded port in your router; this because routers typically don't allow to forward UPD packets as a broadcast!!!

   The WOL gateway solves both issues:
   When the WOL gateway receives a WOL packet (forwarded by the router), it evaluates:
   - If it is a Secure WOL packet (Prefix should be FF:FF:FF:FF:FF:FF)
   - If the SecureOn password matches the SecureOn of your choice.
   Only matching secure WOL packets are broadcasted on your local LAN. 
   - This prevents floading of your LAN due random external UDP packets comming from your router
   - Only a WOL wakeble device on your local LAN that has a matching MAC address will respond (even if it doesn't supportthe SecureOn option)
   Router configuration is simple:
     Give this WOL Gateway device a fixed IP address (assign it via the reserved DHCP list in your router)
     Forward a port of your linking on your router (advice is from 49152 to 65 535 ) and forward only the UDP packets to this device (port 50000)
     See: https://www.utilizewindows.com/list-of-common-network-port-numbers/

*/

#include <ESP8266WiFi.h>                  // istall board manager sw via http://arduino.esp8266.com/stable/package_esp8266com_index.json
#include <ESP8266WebServer.h>
#include <WiFiUdp.h>

////////////////////////////////////////////////
// Class to Send/Receive UDP's:
////////////////////////////////////////////////
class BroadCastDeamon: public WiFiUDP {
  public:
  void PostUDP( const char *Packet, IPAddress ip, unsigned int destPort) {
     PostUDP(Packet, strlen(Packet), ip, destPort);
  }
  void PostUDP( const char *Packet, int len, IPAddress ip, unsigned int destPort) {
    WiFiUDP::beginPacket( ip, destPort);
    WiFiUDP::write(Packet, len);
    WiFiUDP::endPacket();
  }
  void BroadcastUDP( const char *Packet, unsigned int destPort) {
     BroadcastUDP(Packet, strlen(Packet), destPort);
  }
  void BroadcastUDP( const char *Packet, int len, unsigned int destPort ) {
    IPAddress broadcastIP = WiFi.localIP() | (~WiFi.subnetMask());
    PostUDP(Packet, len, broadcastIP, destPort);
  }  
  
  bool CheckReceived() {
    // if there's data available, read a packet and return length
    packetSize = parsePacket();
    if (packetSize)
    {
      //Serial.print("Received UDP packet from [");
      //Serial.print(broadcastUdp.remoteIP());
      //Serial.print("] : ");
      // read the packet into packetBufffer
      int len = read(packetBuffer, 255);
      packetBuffer[len] = 0;
      //Serial.println(packetBuffer);
      return true;
    }
    return false;
  }
  char *GetPacketData() {
    return packetBuffer;
  }
  int GetPacketSize() {
     return packetSize;
  }
  private:
   char packetBuffer[256]; //buffer to hold last packet retreived
   int  packetSize;
};

BroadCastDeamon WOLDeamon;

////////////////////////////////
// the configuration Part of the code:

// SecureOn for WOL has to be 6 hex bytes; Advice change the default.
byte SecureOn[7] = "123456"; // In most WOL apps that support SecureOn you have to enter the bytes in HEX format like this: "31:32:33:34:35:36" for "123456"
// Receiver Port for WOL packets forwarded by the router
unsigned int WOLPort=50000; // just some random choice in the free range. But make sure you forward UDP packets to this port in your router

// Enter your own WLAN credentials
char wlanSSID[]="YourWiFiSSID";
char wlanPWD[]="YourWiFIPWD";


void setup() {
  delay(1000);
  Serial.begin(115200);
  delay(1000);
  Serial.println("Starting the WOL Gateway "); 
  while (!ConnectToNetwork(wlanSSID, wlanPWD)) {
    delay(2000);
  }
  WOLDeamon.begin(WOLPort);
}



void loop() {
    HandleWOLReceived(SecureOn);
    delay(10);
}



bool ConnectToNetwork(char *ssid, char *pass )  {
  int timeout=0;
  bool succes=false;
  WiFi.mode(WIFI_STA);
  Serial.print("Connecting SSID: ");
  Serial.println(ssid);
  WiFi.begin(ssid, pass);
  while (WiFi.status() != WL_CONNECTED && timeout < 10)
  {
      delay(1000);
      timeout++;
  }
  if (WiFi.status() == WL_CONNECTED) {
     Serial.println();
     Serial.print("Connected, IP address: ");
     Serial.println(WiFi.localIP());
     succes=true;
  }
  else {
    Serial.println("Connecting to SSID Failed ");
  }
  // printWifiMode();
  return succes;
}



#define WOL_SECURE_PACKET_SIZE 108
bool HandleWOLReceived (byte *pwd) {
  if (WOLDeamon.CheckReceived() && WOLDeamon.GetPacketSize() == WOL_SECURE_PACKET_SIZE) {
     byte *pPrefix = (byte *)WOLDeamon.GetPacketData();
     byte *pPWD = pPrefix + 102; // start position of the SecureOn
     // check if it is has both a valid WOL prefix (6 times FF) and that it contains the defined SecureOn password
     for (int i=0; i<6; i++) {
       if (pPrefix[i] != 255 || pPWD[i] != pwd[i]) {
         Serial.println("INVALID WOL packet received");
         return false;
       }
     }
     // Broadcast WOL packet on local LAN
     Serial.println("Valid WOL packet received");
     WOLDeamon.BroadcastUDP(WOLDeamon.GetPacketData(), WOL_SECURE_PACKET_SIZE, 7); // accidently used for WOL (official for echo)
     WOLDeamon.BroadcastUDP(WOLDeamon.GetPacketData(), WOL_SECURE_PACKET_SIZE, 9); // (un)official WOL port
  }
  return true;
}

Credits

erkr

erkr

4 projects • 19 followers

Comments