Watchdog para router ADSL con FreeBSD

Autor: Emilio Florido
Fecha: 14/12/2002
Revisión: 16/01/2005


El punto de partida

En mi trabajo tenemos una máquina FreeBSD conectada de forma permanente a Internet mediante un router ADSL. Entre otros, esta máquina da servicio de correo electrónico y páginas Web para el dominio que tenemos contratado, por lo que lógicamente, está continuamente funcionando -el famoso 24x7 que tanto se oye por ahí-

El problema

Aunque después de más de cinco meses de funcionamiento ininterrumpido de la máquina FreeBSD no he tenido con ella ni un sólo problema, no puedo decir lo mismo del router ADSL SpeedStream 5660 que nos conecta con Internet. En realidad este es un aparato "poco conflictivo" en el sentido de que es fácil de configurar y que normalmente pasa desapercibido, si no fuese por la manía que tiene de dejar de trabajar cada 4 ó 5 días de funcionamiento continuo. Eso si, para que vuelva a funcionar no hay más que desconectar la alimentación y volver a conectarla. Un par de minutos después ya está en forma otra vez... claro que si esto ocurre por la noche o en vacaciones, a ver quien va a desconectar y conectar de nuevo el dichoso router.

Que se cuenta aquí

Dado que la máquina FreeBSD está al margen de problemas por funcionamiento continuado, la solución a este problema que aquí planteo consiste en hacer que la máquina FreeBSD esté cada cierto tiempo comprobando el correcto funcionamiento del router ADSL y, en caso de que "note" que ha dejado de funcionar, lo desconecte y conecte de nuevo.

Me gustaría dejar claro que esto es lo que yo he hecho y que al menos hasta ahora funciona perfectamente. Lógicamente no puedo garantizar que funcione en otras situaciones o con otros equipos, pero si estoy abierto a preguntas o sugerencias de cualquier tipo.

Por otra parte, este método exige poder conectarse como administrador a la máquina FreeBSD para compilar un par de ficheros, crear un script, editar el fichero de trabajos periódicos,  y acceder a un puerto de salida como administrador, por lo que siempre se corre el riesgo de estropearlo todo y tener que reinstalar de nuevo… así que ya estás avisado.... No me puedo considerar responsable de cualquier problema que te surja si sigues adelante, pero tampoco tiene por que ocurrir nada de esto, así que tú decides.

También hará falta crear algo de hardware, así que, aunque no es necesario ser un manitas, si que habrá que usar un soldador y atreverse a conectar algunos cables y componentes electrónicos, por lo que si no te apetece "cacharrear" esta solución no te servirá. Es más, como ya comentaba más arriba,  en el peor de los casos, el circuito hardware podría averiar la placa base de tu máquina o algunos de sus componentes al conectarlo, así que como antes decía, tú debes decidir si te atreves con él.

Una solución alternativa

Aunque tanto el router ADSL SpeedStream 5660 como otros muchos incluyen la posibilidad de obligarles a realizar un reseteo por software, este método no puedo recomendarlo personalmente, pues aunque durante un tiempo he estado usándolo, no da la suficiente seguridad como para despreocuparse del tema como si la da el método que luego propondré (aunque lógicamente este método es bastante más sencillo y no necesita de ningún hardware para realizarlo).

La idea básica en este caso es cada cierto tiempo -por ejemplo, una o dos veces al día- obligar desde la máquina FreeBSD, mediante un script que se ejecute a determinadas horas, a que el router realice un reseteo por sotfware.

De esta forma, al menos en teoría, como se rearranca el router debería ser casi lo mismo que resetearlo quitándole y volviéndole a poner la alimentación. Digo al menos en teoría, pues en la práctica la cosa cambia.

Esto es así por que hay que comprender que si el router ADSL se bloquea no sólo no conectará con Internet, si no que tampoco atenderá a ningún comando que se le envíe, y evidentemente, tampoco a un comando de reseteo. Tanto es así, que aunque en mi caso concreto reseteaba el router dos veces al día (una por la noche y otra al medio día), no fue suficiente como para evitar que justo un viernes, por lo visto poco después del reseteo se quedara bloqueado y por tanto sin conectar a Internet y sin atender comandos durante todo el fin de semana hasta que el lunes siguiente fui "en persona" a desconectarlo y volver a conectarlo para que "entrara en razón".

Un primer esquema de lo que pretendemos construir

He aquí un primer esquema muy general para que veas lo que pretendemos construir. Fíjate en que lo único que hará nuestro circuito es "interponerse" entre la fuente de alimentación del router ADSL y el propio router. De esta forma, bajo su control estará el dejar llegar o no la tensión de alimentación al router ADSL.

 

Reuniendo los componentes electrónicos

Dado que este proyecto necesita de una parte hardware, lo mejor es comenzar por adquirir los componentes electrónicos necesarios y realizar el montaje. De esta forma, cuando tengas el hardware a punto, será mucho más sencillo probar y configurar el software que lo maneja.

La lista de componentes necesarios es:

1 Transistor BC337 o equivalente
1 Resistencia 1000 Ohmios
1 Diodo 1N4001 o equivalente
1 Relé de 12 voltios (bastará con que tenga contactos simples)
1 Caja aluminio de 10x5x3 centímetros
1 Cable impresora
50 centímetros de cable paralelo (tipo telefónica por ejemplo)
1 Clavija macho aérea
1 Clavija hembra empotrable

Algunos detalles a tener en cuenta podrían ser; la clavija macho aérea debe ser idéntica a la que use el router ADSL ya que está será la que, cuando todo esté construido, suministrará (o no) la tensión de alimentación al router. La clavija hembra empotrable debe ser justo una que permita conectar en ella la clavija de alimentación que originalmente alimentaba al router ADSL. En cuanto al cable de impresora, en realidad no haría falta más que un conector (concretamente el que conectaría al conector posterior del puerto de impresora de la máquina FreeBSD) con algo así como un metro de cable paralelo conectado en únicamente dos de sus patillas, pues el resto de ellas no nos serán útiles; de todas formas, yo no "escatimaría" en este caso y usaría un cable "normal" de impresora al que cortaría el conector del lado que llega a la impresora de forma que se pueda acceder a sus hilos por separado. Respecto al relé, he sugerido 12 voltios... pero este es mi caso concreto, para ser exactos, el relé debería ser de la misma -o lo más cercana posible- tensión que la que suministre la fuente de alimentación del router.

Por cierto, y aunque no lo incluya en la lista, será necesario un soldador (a ser posible con punta fina y de unos 25 Vatios), algo de estaño y una pistola de silicona para "fijar" todo el montaje a la caja de aluminio (tal vez ver las imágenes de más abajo te de una idea de que se pretende construir).

El esquema electrónico

Una vez reunido todo el material es el momento de unir los componentes. Este es el esquema a realizar:

 

 

En realidad no necesita más que unas 10 soldaduras, aunque lógicamente habrá que tener la precaución de conectar correctamente las tres patillas del transistor, las dos del diodo y las tomas del positivo y el negativo, tanto en la clavija empotrada hembra como en el conector aéreo macho.

En cuanto a ajustes, el circuito no necesita ninguno y debería funcionar a la primera. Tal vez a alguien le apetezca jugar con el valor de la resistencia o buscar otro transistor más adecuado (casi cualquiera del tipo NPN que soporte unos cientos de miliamperios en el colector servirá), pero no lo creo necesario. De hecho, hay que pensar que el relé no actuará más que por periodos de unos 5 segundos y que su estado normal de funcionamiento es permanecer desactivado.

Unas imágenes del montaje ya realizado

Aunque lógicamente se podría realizar el montaje de otra forma, aquí tienes un par de imágenes de como lo he montado yo. Puedes observar que en la caja de aluminio cabe más que de sobra y que para fijarlo me he limitado a "pegar" con silicona el relé a la caja, pues el resto de componentes son tan ligeros y tan pequeños que con las soldaduras de sus propias patillas se quedan suficientemente fijos.

 

Pruebas preliminares

Una vez montado el circuito y revisado un par de veces (más vale asegurarse de que todo está correcto y no lamentarse después) es el momento de hacer un par de pruebas a ver si todo está bien.

En primer lugar, sin conectar la clavija de alimentación de la fuente de alimentación del router a tu circuito, conecta el cable de impresora a la máquina FreeBSD. Si todo va bien, no debería ocurrir absolutamente nada. Si ves (u oyes) que el relé se mueve o la máquina hace cualquier comportamiento extraño, desconecta y revisa donde te has equivocado (pues es seguro que te has equivocado).

En segundo lugar, desconecta el cable de la impresora de la máquina FreeBSD y únicamente conecta la fuente de alimentación del router a tu circuito. Si todo va bien, no debería ocurrir absolutamente nada. Si ves (u oyes) que el relé se mueve desconecta y revisa donde te has equivocado (pues de nuevo es seguro que te has equivocado).

Muy bien, pues ya no hay nada más que comprobar y es el momento de la verdad. Conecta tanto la fuente de alimentación del router a tu circuito como el cable de la impresora a la máquina FreeBSD. Si todo va bien, nuevamente no debería ocurrir absolutamente nada excepto que si el router ADSL también lo has conectado usando la clavija macho aérea de nuestro circuito, este debería encenderse normalmente.

Ahora es el momento de "dar vida" a nuestro circuito, y para eso ahí van un par de programas en C que usaremos, no solo ahora para comprobar que todo va bien, si no más adelante como parte básica del watchdog para el router ADSL.

El primero de ellos lo he llamado conecta_rele.c y es el siguiente:

// Emilio (14-12-02)
// Activa el bit 0 del puerto de la impresora
#include <stand.h>
#include <machine/cpufunc.h>

#define IMPRESORA 0x378
#define ACTIVA 0x01

int main(void)
{
  i386_set_ioperm(IMPRESORA,1,1);
  outb(IMPRESORA, ACTIVA);
  usleep(100000);
  i386_set_ioperm(IMPRESORA,1,0);
  return (0);
}

Y el segundo lo he llamado desconecta_rele.c y este es su listado:

// Emilio (14-12-02)
// Desactiva el bit 0 del puerto de la impresora
#include <stand.h>
#include <machine/cpufunc.h>

#define IMPRESORA 0x378
#define DESACTIVA 0x00

int main(void)
{
  i386_set_ioperm(IMPRESORA,1,1);
  outb(IMPRESORA, DESACTIVA);
  usleep(100000);
  i386_set_ioperm(IMPRESORA,1,0);
  return (0);
}

Como imaginarás, por lo reducido que son y por las pocas instrucciones que tienen no deberían dar el más mínimo problema para compilar. No será necesario más que teclear para el primero:

gcc conecta_rele.c -o conecta_rele

y para el segundo:

gcc desconecta_rele.c -o desconecta_rele

con lo que ya podremos ver si realmente funciona el circuito, pues ejecutando el primero de ellos deberemos oír como se conecta el relé y si tenemos el router ADSL unido a nuestro circuito debería desconectarse... si has leído bien, no me he equivocado, justo cuando el relé se conecta el router ADSL debe apagarse... de esta forma conseguimos que la mayor parte del tiempo, es decir, durante el funcionamiento normal del router ADSL el relé (y todo nuestro circuito) esté en reposo y tan sólo durante los momentos de reseteo del router será cuando nuestro circuito entre en funcionamiento.

Como ya has conseguido conectar el relé, ahora es el momento de desconectarlo. Ejecuta el segundo de los programas y deberías oír como se desconecta el relé, y si tenías el router ADSL también conectado al circuito deberás ver como de nuevo entra en funcionamiento.

Si has llegado hasta aquí, enhorabuena, por que el resto que te queda por hacer es de lo más sencillo. Pero antes, un par de detalles técnicos para los más "puristas".

Situando el puerto de la impresora

Si has ojeado el código de los dos programas en C que daba más arriba verás que hay en ellos un par de suposiciones que suelen cumplirse.

Por una parte, el puerto de la impresora he partido de que está situado en la dirección hexadecimal 0x378, tal y como queda determinado por la línea:

#define IMPRESORA 0x378

Imagino que en más del 90% de los casos eso es así, pero si no te funcionara el montaje y todo el cableado lo has revisado y crees que es correcto, podría ocurrir que en tu caso concreto el puerto de la impresora estuviese situado en otra dirección.

Para "adivinarla" tienes una opción muy sencilla. Durante el arranque de la máquina, o bien fíjate en los mensajes de la BIOS (deberías buscar algo así como PARALELL PORT 378 por ejemplo) y el número que ahí veas es el que debes sustituir en los códigos fuentes de ambos programas en C o bien, entra en la configuración de la BIOS (normalmente pulsando la tecla ESC, o la tecla F2, o la tecla Supr, etc) y busca entre los apartados disponibles uno parecido a INTEGRATED PERIPHERICAL y probablemente ahí esté la dirección correcta del puerto de la impresora para tu máquina concreta.

Por otra parte, para activar el circuito yo he usado el bit 0 del puerto de la impresora. No se me ocurre ningún motivo para usar otro bit de ese puerto, pero si a ti si, no tendrías más que cambiar la línea:

#define ACTIVA 0x01

del primero de los programas por una que active el bit que a ti te interese. Eso si, recuerda que además de esta línea del código, en el circuito deberás usar en lugar de la patilla 2 del conector de la impresora la patilla que corresponda en función del bit que elijas.

Puedes pasarte, por ejemplo por http://www.fortunecity.com/skyscraper/neil/534/paralelo.htm o por http://www.pchardware.org/HWB/co_ParallelPC.html, y así ver el detalle completo del patillaje del conector de la impresora.

Vigilando el router ADSL

Ahora que ya funciona nuestro circuito, es el momento de pensar en como vigilamos el router ADSL desde la máquina FreeBSD para poder saber si está funcionando correctamente y en caso contrario obrar en consecuencia.

Aunque imagino que se podría recurrir a algo más complejo, en mi caso concreto he recurrido a algo tan simple como a enviar un comando PING al router ADSL. Si responde al comando PING, todo está correcto y no hay más que hablar.

Si por el contrario no responde, será el momento de desconectarle la alimentación, esperar un cierto tiempo y volver a conectársela. Ojo a esto de esperar un cierto tiempo, pues por una parte, si le quitamos la alimentación durante muy poco tiempo y la volvemos a restaurar enseguida correríamos el peligro de que la propia inercia de su funcionamiento (por llamar de alguna forma a la carga que quede en sus componentes) haga que no se resetee correctamente y por tanto que no volviese a funcionar.

De todas formas, y para quien guste de experimentar, he incluido el tiempo de espera entre la conexión y la desconexión como una variable en el script que está a continuación... aunque con el valor que tiene por defecto, al menos con el router SpeedStream 5660 funciona perfectamente.

Ahí va el script que vigila el router y que actúa en consecuencia en caso de problemas. Lo he llamado  comprueba y este es su listado:

#!/bin/sh

# Emilio (14-12-02) - Revisado (12-01-05)
# Comprueba el estado del router ADSL y si "no responde"
# lo desconecta y lo vuelve a conectar

# Dirección IP del router ADSL
IP_ROUTER=192.168.1.1

# Tiempo de espera desde que se desconecte hasta que
# se vuelva a conectar el router ADSL (en segundos) 
TIEMPO_DESCONEXION=5

# Ruta a donde estén los programas 
# de conexión y desconexión del relé
RUTA=/root

# Usuario al que se notificará cada vez
# que sea necesario resetear el router ADSL
DESTINATARIO=root


# A partir de aquí ya no hay nada que ajustar

ESTADO=`/sbin/ping -Q -n -q -c 2 -t 5 $IP_ROUTER &> /dev/null && echo "Funcionando" || echo "No responde"`

if [ "$ESTADO" = "No responde" ] ; then
$RUTA/conecta_rele
sleep $TIEMPO_DESCONEXION
$RUTA/desconecta_rele
echo "Ha sido necesario resetear el router ADSL" | mail -s "Router ADSL" $DESTINATARIO
fi

Como ves, también hay un par de variables más que podrás retocar además de la del TIEMPO_DESCONEXION que comentaba más arriba.

Estas son, por una parte la IP_ROUTER que como verás en mi caso es 192.168.1.1. Ahí deberás poner la dirección IP de tu router... me refiero lógicamente a la que "mira" hacia tu red y no la pública... (al final puedes leer una aportación que he recibido a este respecto).

Por otra parte, los programas que compilamos antes para conectar y desconectar el relé deberán estar en algún directorio concreto, pues en la variable RUTA deberás poner su valor (en mi caso, como ves los he dejado en el propio directorio del root, ... si ya se que soy algo vago, pero en fin...).

Ah, y casi se me olvidaba, no está de más que cuando el circuito deba actuar avise de ello a algún usuario. Lo más normal es que sea al root pero con la variable DESTINATARIO podrás avisar a quien quieras. Si te preguntas que porqué avisa por correo electrónico, te diría que no me apetecería que la máquina FreeBSD me avisara de ninguna otra forma de que por ejemplo un jueves a las 4:15 de la mañana tuvo que resetear el router ADSL.

Por cierto, si te preguntas por qué tantos parámetros para el comando PING, además de sugerirte man ping te diría que me pareció razonable dar una oportunidad al router ADSL de "perder" un PING por alguna razón siempre que reaccione al siguiente y no forzar a un reseteo a no ser que pierda dos PING's seguidos (de ahí el  -c 2 ). Los demás parámetros tendrás que mirarlos en el man si quieres saber para que son.

Si quieres ver que tal funciona el script, ejecútalo sin miedo. Si el router está conectado y funcionando correctamente (que será lo normal), no debería ocurrir absolutamente nada. Para comprobar que realmente se da cuenta de que el router no funciona bastará con que desconectes el cable de red del router (¡no el del teléfono!) y lo vuelvas a ejecutar. Si todo va bien verás que el router se apaga, pasan unos 5 segundos, y se vuelve a encender. Además, si tecleas mail deberás tener un mensaje de correo electrónico pendiente de leer. Por cierto, no te olvides de reconectar el router nuevamente a la red...

Cuando comprobar

Con el script anterior ya podemos no sólo saber si el router ADSL ha dejado de funcionar si no que además lo desconectará y conectará de nuevo e incluso nos avisará de ello por correo electrónico.

Ahora ya solo queda "convencer" a la máquina FreeBSD para que ejecute cada cierto tiempo este script de forma automática. Nada mejor para esto que usar los servicios de cron.

Aunque en un principio se podría estar tentado de estar constantemente comprobando que el router ADSL funciona bien, tampoco hay que ser "tan pesados". En mi caso concreto, con una comprobación cada cinco minutos me ha parecido más que suficiente. De todas formas no estará de más que te avise de que, independientemente del ínfimo aumento de carga a la red o la máquina FreeBSD que podría ocasionar el acelerar la frecuencia de comprobación, existe otro problema bastante más serio si las comprobaciones se hacen demasiado a menudo.

Imagina que hacemos una comprobación cada minuto y que el router realmente necesita ser conectado y desconectado. Deberías recordar del apartado anterior que decía que entre la desconexión y la nueva conexión debemos dejar transcurrir algún tiempo. Si a este tiempo sumamos lo que el router tarda en rearrancar podría ocurrir que juntos sumasen más de un minuto... claro que la máquina FreeBSD realizaría otra comprobación y lógicamente el router no respondería tampoco (ya que aún no habría tenido tiempo de rearrancar). Si esto ocurre, el circuito entraría en una especie de "ciclado" del que nunca llegaría a salir.

Por tanto, no aconsejo bajar de esos 5 minutos que antes proponía entre comprobación y comprobación. Partiendo por tanto de aquí, teclea:

crontab -e

y en el editor que se abre (imagino que vi), escribe lo siguiente:

SHELL=/bin/sh
MAILTO=""
*/5 * * * * /root/comprueba

Como verás no hay demasiado "truco". Cada 5 minutos (de todas las horas de todos los días, de ahí los asteriscos) se llama al script de comprobación y ya este obrará en consecuencia.

Aportaciones

Hace ya algún tiempo -justo después de publicar el artículo- recibí algunos mensajes electrónicos que me comentaban la posibilidad de que el problema que estaba teniendo con el router SpeedStream 5660 se debiesen a algún mal diseño de su firmware y que tal vez actualizándolo, se resolvería. Agradezco a cuantos me lo sugirieron su buena intención (incluso algunos mensajes incluían referencias en la Web a tal firmware), pero desafortunadamente, el dichoso router YA lo había actualizado un par de veces antes de decidirme a crear este circuito y no había obtenido ningún resultado positivo (es decir, cuando le parece, se "bloquea" y no hay más que hablar en tanto no se desconecte y se vuelva a conectar).

Recientemente, Antonio Vanegas me ha comentado un par de "deslices" en el código del script comprueba. Por una parte, faltaban un par de "comillas" en una de las sentencias... pero sobre todo, faltaba un carácter & que según me comentó Antonio le proporcionó un buen quebradero de cabeza hasta dar con él. Desde aquí -y como ya he hecho "en persona"- le vuelvo a pedir disculpas por estas omisiones, pero lo curioso del caso es que ni en FreeBSD 4.8, ni en 4.9 ni siquiera en 4.10 el script comprueba me había dado a mi ningún problema... sin embargo, Antonio usa FreeBSD 5.3 y quizás ahí resida el "misterio". Es más, tampoco en los fuentes en "C" de conecta y desconecta yo hacía ninguna "espera" ni "cerraba" el puerto de la impresora después de usarlo, y sin embargo, Antonio ha encontrado que para FreeBSD 5.3 esto si es necesario. De todas formas, tanto él como yo creemos que ahora el código es correcto para cualquier versión de este estupendo sistema operativo.

También Antonio me comentó que el router que él utiliza -un GreatSpeed BG240- cuando se "bloquea" pierde la conexión con Internet (es decir, con la red externa) pero sigue respondiendo a los PING's de la red interna... como oportunamente me indicó Antonio, en este caso, basta con sustituir en el mismo script comprueba el valor de la variable IP_ROUTER que yo inicializaba a 192.168.1.1 por una dirección cualquiera del "exterior", por ejemplo, Antonio usa la IP de Google (quizás en este caso, IP_DE_REFERENCIA hubiese sido un nombre más acertado para esta variable). Desde aquí, nuevamente, gracias por tus aportaciones Antonio.

Agradecimientos

Bueno, pues hasta aquí llega este proyecto. Espero que te proporcione tan buen servicio como a mi me proporciona. Te sorprenderá ver cuantas veces es necesario desconectar y volver a reconectar el router...

Por último, como otras veces, no estará de más desde aquí mostrar mi agradecimiento a toda la gente que hace posible que FreeBSD sea un sistema operativo tan fiable como para estar días y días funcionando ininterrumpidamente sin el menor problema.

 


Emilio Florido (eflorido@iesmajuelo.com)