Repetidor WakeOnLan para Raspberry

Wake-on-LAN (WOL) es un estándar pensado para encender remotamente un ordenador por medio de un mensaje de red. Básicamente consiste en enviar a toda la red local un paquete de datos (conocido como "Magic Packet") que contiene la MAC del equipo que queremos encender. Cuando la tarjeta de red detecta un paquete con su MAC da la orden de encendido del resto del equipo.

Hasta aquí todo es muy sencillo, este paquete lo podemos enviar por UDP a la dirección 255.255.255.255 de tal manera que llegue a toda nuestra red local, o a una dirección del tipo 192.168.1.255 si queremos afinar un poco más. El problema surge cuando queremos enviar este paquete desde internet, desde un móvil por ejemplo, para encender un equipo de nuestra casa. La mayoría de los router caseros no permiten hacer redirecciones de este tipo, es decir recibir un paquete UDP por un puerto y redirigirlo a una dirección del tipo 192.168.1.255, y dirigirlo a la ip del equipo tampoco tiene sentido porque recordemos que el equipo esta apagado, por lo que no esta haciendo uso de ninguna ip en ese momento.

La solución que se me ocurrió en mi caso fue utilizar una placa Raspberry Pi, un minúsculo ordenador que funciona con linux, como repetidor del "Magic Packet", de tal manera que cuando le llega el mensaje de arranque a un puerto UDP determinado el lo reenvía a toda la red local, encendiendo de este modo el equipo que corresponda. Os preguntareis que ganamos con esto, ya que seguimos teniendo que tener un ordenador, en este caso el Raspberry, continuamente encendido, la respuesta es que este pequeñisimo ordenador consume muy poco, menos de 5W frente a los mas de 200w que puede consumir un ordenador normal en reposo, ademas de no tener ningún molesto ventilador girando constantemente. Por otro lado podemos utilizar el Raspberry para un montón de aplicación más, por lo que no solo lo tendríamos encendido por este motivo.

Para montar el repetidor primero tenemos que instalar xinetd, que es el que se va a encargar de "escuchar" por el puerto UDP y va a ejecutar nuestro programa cuando reciba un paquete.

sudo apt-get install xinetd

Ahora creamos en el directorio /etc/xinetd.d un fichero llamado wolrelay con lo siguiente:

service wolrelay 
{
    log_type = FILE /var/log/wolrelay
    log_on_success = HOST PID EXIT
    disable = no
    port = 1999
    socket_type = dgram
    protocol = udp
    wait = yes
    user = pi
    nice = 10
    type = UNLISTED
    server = /usr/bin/wolrelay
    server_args = 192.168.1.255 9
}

La ruta del ejecutable y la IP pueden variar dependiendo de donde guardes el ejecutable una vez compilado, y del rango de tu red.

El siguiente paso es crear el ejecutable, el código es el siguiente:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/select.h>
 
int main(int argc, char *argv[])
{  
 
  int i, sock;
  struct {
    char header[6];
    char mac[16][6];  
  } packet;  
  struct sockaddr_in address;
 
  // Comprobamos los parametros 
  if (argc != 3)
    return 1;
  // Leemos el paquete, y comprobamos su tamaño
  if (fread(&packet, 1, sizeof(packet), stdin)==sizeof(packet)) {
    // Comprobamos la cabecera del paquete
    if (strncmp(packet.header,"\xFF\xFF\xFF\xFF\xFF\xFF",6)!=0)
      return 3;
 
    // Comprobamos que las 16 repetciones de la MAC son iguales    
    for (i=1;i<16;i++) {
      if (strncmp(packet.mac[0],packet.mac[i],6)!=0)
      return 4;
    }
 
    // Creamos un socket para reenviar el paquete
    sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (sock < 0) 
      return 5;
 
    i=1;
    // Configuramos el socket para que pueda hacer broadcast
    if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (void *) &i, sizeof(i)) != 0) {
      // Si no podemos configurar el socket lo cerramos y salimos
      close(sock);
      return 6;	
    }	
 
    // Configuramos la direccion y puerto de envio      
    address.sin_family = AF_INET;
    address.sin_port = htons(atoi(argv[2]));
    if (inet_aton(argv[1], &address.sin_addr) == 0) {
      // Si la direccion no es correcta, cerramos el socket y salimos
      close(sock);
      return 7;	
    }	
 
    // Reenviamos el paquete recibido a la direccion de envio
    if (sendto(sock, &packet, sizeof(packet), 0, (struct sockaddr *) &address, sizeof(address)) < 0) {
      // Si el envio no es correcto, cerramos el socket y salimos
      close(sock);
      return 8;	
    }	  
 
    // Cerramos el socket y salimos sin errores
    close(sock);
    return 0;
  } else
    return 2;  
}

Ahora solo hay que copiar el nuevo ejecutable a su nueva ubicación y reiniciar xinetd

sudo cp wolrelay /usr/bin
sudo /etc/init.d/xinetd restart

Hay que recordar también abrir un puerto UDP en el router y dirigirlo hacia el Rasberry.

Enlaces de interes:
http://es.wikipedia.org/wiki/Raspberry_Pi
http://www.linuxfocus.org/English/November2000/article175.shtml

Comentarios

Interesante.
Gracias por la información.

hola
mi pregunta es donde o con que programa creas el ejecutable?

saludos!

Pues con el gcc:

gcc -o wolrelay wolrelay.c