El arte de programar
Wake On LAN y Magic Packet
Junio 13th, 2005 - [Enlace local]
Todos sabemos que podemos apagar el ordenador con un comando o bién con una combinación de pulsaciones de teclado o ratón, también hay programas que dado cierto evento hacen que se apague el sistema.
Esto no tiene ningún misterio, pero... ¿y si se trata de encenderlo?. Puede que con el solo hecho de pulsar el botón de encendido de la cpu nos sea suficiente (a veces con pulsar un botón del teclado o mover el ratón), sin embargo cuando no estamos físicamente presentes la cosa cambia: un cibercafe donde es más cómodo encender todos los ordenadores a la vez, o encender un ordenador remoto que por las noches se apaga para ahorrar electricidad, etc. Antiguamente los ordenadores se apagaban y punto, pero desde que irrumpió en el mercado el formato ATX de cajas y placas madre y con el estandar ACPI la cosa no ha hecho más que facilitarnos las cosas: ahora gracias a esos dos estandares somos capaces de apagar el ordenador por software, apagar el equipo ordenadamente por hardware, suspender, volver a reactivar, volver a encender, etc. En la parte que nos toca hay unos eventos que son capaces de encender un ordenador cuando este está apagado, y para la gestión remota de ese encendido se puede usar unos metodos denominados wake on ring, wake on modem (por modem) o wake on lan (por red).
Este artículo se centra en el método Wake On Lan (+ o - despertar por red). Este sistema se basa en que un ordenador cuando se apaga, no lo hace totalmente, sino que la fuente de alimentación sigue alimentando a la placa madre, y esta puede gestionar con muy bajo consumo los eventos que se den, como por ejemplo, encender el ordenador cuando recibe un paquete especial por la tarjeta de red. Este paquete, conocido como Magic Packet es muy simple y su formato consiste en 6 bytes con valor hexadecimal FF y otros 16 grupos de 6 bytes con la dirección MAC de la tarjeta de red. Da igual en que protocolo se envié ese paquete (IPX, TCP/IP, etc), con tal de que se envíe por toda la red. Así si se quiere encender un ordenador con una MAC 01:02:03.04:05:06 se enviaría un paquete con estos bytes en hexadecimal:
FFFFFFFFFFFF
010203040506010203040506010203040506010203040506
010203040506010203040506010203040506010203040506
010203040506010203040506010203040506010203040506
010203040506010203040506010203040506010203040506
El paquete debe enviarse a toda la red, por lo que hay que intentar que sea con el protocolo que se haga, este debe permitir enviar paquetes broadcast. Esto normalmente se puede conseguir con el protocolo IP, ya que poniendo como destinatario la dirección IP 255.255.255.255 debería llegar a todos los nodos de todas las redes que lo permitan. El TCP o UDP que venga por encima no es importante, aunque personalmente prefiero el UDP por no ser orientado a conexión. Por encima del UDP vendrá el magic packet. A continuación pongo un ejemplo de codigo fuente en varios lenguajes de programación para que podais probarlo (son tan cortos que no hacen falta comentarlos, tampoco tienen gestión de errores para su simplicidad), en vuestro caso solo tendrías que modificar el puerto (1976) y la dirección MAC (01:02:03:04:05:06):
C para Linux/Unix y Windows
#ifdef WIN32
#include <stdio.h>
#include <winsock2.h>
#else
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#endif
void main(void)
{
int conexion;
struct sockaddr_in direccion;
unsigned char buffer[102];
unsigned char mac[]={0x01,0x02,0x03,0x04,0x5,0x06};
int valor=1,contador;
#ifdef WIN32
WSADATA wsaData;
WSAStartup( MAKEWORD( 2, 2 ), &wsaData );
#endif
for(contador=0;contador<6;contador++)
buffer[contador]=0xff;
for(contador=6;contador<102;contador+=6)
memmove(&buffer[contador],mac,6);
memset(direccion.sin_zero,0,8);
direccion.sin_family=AF_INET;
direccion.sin_addr.s_addr=htonl(INADDR_BROADCAST);
direccion.sin_port=htons(1976);
conexion=socket(AF_INET,SOCK_DGRAM,0);
valor=setsockopt(conexion, SOL_SOCKET, SO_BROADCAST, (char *)&valor, sizeof(int));
sendto(conexion,(char *)buffer,102,0,(struct sockaddr *)&direccion,sizeof(struct sockaddr_in));
#ifdef WIN32
WSACleanup();
#endif
}
Perl
use strict;
use IO::Socket::INET;
my $socket;
my $msg;
my $contador;
$socket=new IO::Socket::INET->new(PeerPort=>1976,
Proto=>'udp',
PeerAddr=>'255.255.255.255',
Broadcast=>1
);
$msg="";
for($contador=0;$contador<6;$contador++)
{
$msg.="\xff";
}
for($contador=0;$contador<16;$contador++)
{
$msg.="\x01\x02\x03\x04\x05\x06";
}
$socket->send($msg);
Java
import java.net.*;
import java.io.IOException;
public class Wol
{
public static void main(String args[])
{
byte mac[]={0x01,0x02,0x03,0x04,0x05,0x06};
byte buffer[]=new byte[102];
byte contador;
for(contador=0;contador<6;contador++)
buffer[contador]=(byte)0xff;
for(contador=6;contador<102;contador+=6)
System.arraycopy(mac,0,buffer,contador,6);
try
{
DatagramSocket udp=new DatagramSocket();
udp.setBroadcast(true);
udp.send(new DatagramPacket(buffer,102,InetAddress.getByName("255.255.255.255"),1976));
}
catch(java.net.SocketException se)
{
System.err.println(se);
}
catch(java.net.UnknownHostException ue)
{
System.err.println(ue);
}
catch(java.io.IOException ie)
{
System.err.println(ie);
}
}
}
Visual Basic (Para que funcione debeis insertar un control Winsock)
Dim buffer() As Byte
Dim contador As Byte
ReDim buffer(101) As Byte
For contador = 0 To 5
buffer(contador) = &HFF
Next
For contador = 6 To 101 Step 6
buffer(contador) = &H1
buffer(contador + 1) = &H2
buffer(contador + 2) = &H3
buffer(contador + 3) = &H4
buffer(contador + 4) = &H5
buffer(contador + 5) = &H6
Next
Winsock1.Protocol = sckUDPProtocol
Winsock1.RemoteHost = "255.255.255.255"
Winsock1.RemotePort = 1976
Winsock1.SendData buffer
Por supuesto no hay método que sirva para marcar todas las tarjetas a la vez, así que habrá que ir una por una mandando el magic packet para que se vaya encendiendo cada ordenador. La tarjeta de red de cada ordenador apagado compara todos los paquetes que le llegan del tráfico de red para ver si contiene 16 veces su dirección MAC y así mandar a la placa madre que encienda el ordenador.
Las tarjetas de red que vengan incorporadas ya de fábrica en la placa madre ya tienen esta función activada. Sin embargo las que estan sueltas en ranuras PCI o ISA, deben:
1) Permitir el wake on lan (no todas tienen un firware con esa posibilidad)
2) La placa madre debe tener un conector de 3 pines que permita este evento.
3) Un cable que vaya desde la tarjeta hasta la placa madre.
En ambos casos se debe activar en la BIOS el mantenimiento de energía y posteriormente el evento WAKE ON LAN.
En mi portatil no puedo probar esta característica (cuando se apaga este, la tarjeta ethernet incorporada también se apaga). En el ordenador de sobremesa no me ha funcionado (tampoco funcionan eventos como el de encender el ordenador con una pulsación del teclado) y con el servidor me ha funcionado perfectamente.
El Magic Packet también se puede hacer desde Internet:
Si teneis conexión directa solo debeis modificar vuestro programa para que apunte a vuestra IP pública de Internet (si no es fija podeis usar cosas como dyndns).
Si no teneis conexión directa o no quereis usar un programa, podeis hacerlo desde aquí.
Sin embargo, a no ser que tengais un router que este conectado a Internet y donde podais poner rutas estáticas con IP, puertos y direcciones MAC no podreis hacerlo funcionar, ya que el router (como el mio) borra de la tabla arp las relaciones MAC/IP cuando detecta que no hay tráfico en una tarjeta determinada, por lo que aunque reciba un paquete que luego deba reenviar a una ip interna, lo ignora al no encontrarlo en la tabla; tampoco tiene opción para redireccionarlo como broadcast dentro de la red si no sabe que hacer con el.
» Leer más, comentarios, etc...