lunes, 13 de octubre de 2014

Detectando intrusos

Hace unos días que me ha llegado la última remesa de juguetes para cacharrear con Arduino. Entre ellos hay un sensor de movimiento que ya hace tiempo que me apetecía probar. Es un sensor PIR que son las siglas de Passive Infrared, o lo que es lo mismo un detector de infrarrojos. El dispositivo detecta las variaciones de luz infrarroja y altera el voltaje del pin de salida cuando se produce. El resultado es que podemos detectar la presencia humana, o de cualquier ser vivo, ya que la radiación infrarroja no es más que calor. Al pasar por delante del sensor alteramos la radiación infrarroja detectada y salta la alarma. Vamos a empezar con un montaje básico para irlo complicando en sucesivas entradas hasta construir un sistema de alarma que nos envíe un aviso al móvil cuando detecte movimiento. Para el montaje de hoy vamos a usar un led, un piezo y el sensor PIR. El esquema es el siguiente:



Y aquí el esquema montado en la placa de prototipo



El funcionamiento del circuito es muy simple, cuando detecte movimiento encenderá el LED y el piezo emitirá un sonido. Aquí tenemos una imagen del PIR visto por la parte de abajo:


Como podemos ver, la información sobre la función de cada pin brilla por su ausencia. En este caso en concreto, el pin de la izquierda es GND, el del centro es el de datos y el de la derecha es VCC. Tenemos además los jumpers de la parte inferior izquierda que sirven para determinar cómo responderá el pin de datos si el evento de detección de movimiento se repite. Se puede configurar de modo que el pin de datos esté siempre en alto cuando detecte movimiento constantemente, o para que al detectar movimiento devuelva un pulso a HIGH y después vuelva a LOW, de forma que al detectar movimiento repetidamente devuelva una secuencia de unos y ceros. El de la foto está configurado del segundo modo. Para que esté siempre en alto solo hay que cambiar el jumper para que abarque los dos pines de arriba. Y por último, los dos tornillos anaranjados de la parte inferior sirven para modificar la distancia a la que detectará la variación de infrarrojos, es decir, lo cerca que tienes que estar para que te detecte, y el tiempo de la variación.
Y a continuación el sketch:

const byte sensorPin=2;
const byte ledPin=4;
const byte piezoPin=8;

const char contrast=57;

void setup(){
  Serial.begin(115200);
  pinMode(sensorPin, INPUT);
  pinMode(ledPin, OUTPUT);
  pinMode(piezoPin, OUTPUT);
  Serial.println("Inicializacion OK");
}

void loop(){
    if(digitalRead(sensorPin)){
      digitalWrite(ledPin,HIGH);
      tone(piezoPin,3000,10);
    }
    else{
      digitalWrite(ledPin,LOW);
    }
    delay(250);
     


El código es muy sencillo. Simplemente consulta el estado del sensor cada 250 milisegundos y enciende el led y hace sonar el piezo si se detecta movimiento. En caso contrario se apaga el led.


jueves, 6 de febrero de 2014

Mudanza

Recientemente he mudado mi estudio desde mi antigua habitación en casa de mis padres en la que me alojaba provisionalmente a la que va a ser mi nueva casa cuando la reforme. Echaba de menos tener una mesa grande en un comedor amplio con luz natural para trabajar en mis cosas. Y ya puestos he resuelto un problema que hace ya un tiempo que me atormentaba. Como la estancia en casa de mis padres iba a ser temporal y mi hermana vive a dos casas, decidí no contratar una conexión a internet y usar su wifi. Cuando me da la clave ya me llevo la primera sorpresa. Su wifi está cifrada con WEP. En mi antigua casa mi portátil tardó once segundos en encontrar la clave de un vecino que también usaba WEP. Si mis datos van a viajar por su red más vale que tenga algo más de seguridad, así que cambio la clave, le pongo cifrado WPA2 y desactivo el WPS que también es un colador. Voy a mi habitación y conecto el portátil, llega poca señal pero no importa para mis quehaceres habituales no necesito más que unos pocos KB de ancho de banda. Los próximos días son un suplicio de cortes de conexión. Me desespero, cambio la wifi de canal, sigue habiendo cortes, whatsapp a mi hermana: Reinicia el router. Funciona durante un rato. Tengo que hacer algo. La solución lógica (y fácil) es instalar un repetidor wifi. Pero esta solución tiene varios problemas:

  1. Es fácil, y a un servidor en cuestiones de tecnología no le suelen gustar las cosas fáciles.
  2. Tengo en casa todo lo necesario para resolverlo y por lo tanto me parece un gasto inútil comprar un nuevo aparato teniendo cacharros viejos que aún funcionan y que te pueden solucionar la papeleta.
  3. No es divertido.
Así que me pongo manos a la obra. La idea es tener un router más cerca de mi habitación conectado con el de mi hermana para que mi portátil tenga una señal más fuerte. El router que tenía en mi antigua casa puede servir, ya que tiene la opción de funcionar en modo bridge. El problema es que el de mi hermana no. Por suerte tengo una Raspberry. La Raspberry es un mini ordenador poco potente y de bajo consumo, ya que tiene arquitectura ARM (la misma que los móviles) y al no tener partes móviles (ventilador, disco duro, etc) y una baja frecuencia de procesador (700 MHz)  necesita muy poca energía para funcionar. Esto lo hace ideal para tenerlo funcionando 24 horas al día como puente entre los dos routers. En mi caso también como lo tengo como servidor de descargas bittorrent y emule y como sistema de videovigilancia con una webcam, pero esto ya os lo cuento otro día.
El montaje definitivo es el siguiente:


La Raspberry usa como sistema operativo Raspbian, una versión modificada de Debian para arquitectura ARM. Para conectarla a la wifi usaremos un adaptador USB que tenía muerto de risa en casa. Lo primero que tenemos que hacer es conectarlo al puerto USB y averiguar qué dispositivo es. Para ello usaremos el comando lsusb que nos mostrará todos los dispositivos USB conectados:

Bus 001 Device 006: ID 0ace:1215 ZyDAS ZD1211B 802.11g
Bus 001 Device 005: ID 046d:082b Logitech, Inc.
Bus 001 Device 004: ID 1a40:0101 TERMINUS TECHNOLOGY INC. USB-2.0 4-Port HUB
Bus 001 Device 003: ID 0424:ec00 Standard Microsystems Corp.
Bus 001 Device 002: ID 0424:9512 Standard Microsystems Corp.
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

Nuestro adaptados wifi es un Zydas. Lo buscamos en el repositorio de paquetes con el comando:

 apt-cache search zydas

que nos devuelve un único paquete que contiene el firmware de nuestro adaptador:

zd1211-firmware - Firmware images for the zd1211rw wireless driver

Lo instalamos con el comando:

sudo apt-get install zd1211-firmware

Una vez instalado tenemos que cargar el módulo zd1211rw con el comando

modprobe zd1211rw

Y para comprobar si se ha cargado usaremos el comando

lsmod|grep zd1211rw

que si todo es correcto dará la siguiente salida que nos indica que el módulo se ha cargado en el kernel

zd1211rw               55298  0
mac80211              273413  1 zd1211rw
cfg80211              184163  2 zd1211rw,mac80211

Para que cargue el módulo automáticamente en el arranque tenemos que añadir esta línea al fichero /etc/modules

zd1211rw

Una vez tenemos el módulo cargado tenemos que configurarlo para que conecte a la wifi del router 2. Para ello necesitamos instalar el paquete wpasupplicant:

apt-get install wpasupplicant

y por último crear el archivo de configuración, que en mi caso es /etc/wpasupplicant.conf, con el siguiente contenido:

network={
        ssid="Nombre de la red wifi"
        scan_ssid=1
        psk="Clave de la red wifi"
}

Ahora ya tenemos lista nuestra Raspberry para conectar con los comandos

wpa_supplicant -B -iwlan0 -c/etc/wpa_supplicant.conf
dhclient wlan0

El siguiente paso es convertir nuestra Raspberry en un router, es decir que acepte peticiones por la tarjeta de red y las reenvíe a internet por el adaptador wifi. El router 2 le ha dado a nuestro adaptador la dirección 192.168.0.250. Siempre le va a dar la misma porque la tengo reservada en el router ya que entro por ssh desde fuera de la red. La tarjeta de red está configurada con la dirección 192.168.2.4 como se puede ver en el fichero /etc/network/interfaces

iface eth0 inet static
address 192.168.2.4
netmask 255.255.255.0
broadcast 192.168.2.255
network 192.168.2.0
dns-nameservers 8.8.8.8

Con lo cual el router 1 y los equipos conectados a él deben estar en ese rango de IP y tener como gateway la dirección 192.168.2.4, que es la de la tarjeta de red de la Raspberry. Un detalle importante es que el router 1 tiene desactivado el servidor DHCP, ya que si no se asignaría a sí mismo como gateway en lugar de a la Raspberry. Esto implica asignar las IP de los equipos conectados al router 1 como estáticas, lo cual otrorga un poco más de seguridad a nuestra wifi ya que un posible intruso debería conocer el rango de IP que tenemos asignado. Y por último tenemos que escribir las reglas de filtrado para que redirija el tráfico desde nuestra red hacia internet. Lo haríamos con el siguiente script que se ejecutaría al inicio:

iptables -F
iptables -t nat -F

iptables -P INPUT ACCEPT
iptables -P OUTPUT ACCEPT

export LAN=eth0
export WAN=wlan0

iptables -I INPUT 1 -i ${LAN} -j ACCEPT
iptables -I INPUT 1 -i lo -j ACCEPT
iptables -A INPUT -p UDP --dport bootps ! -i ${LAN} -j REJECT
iptables -A INPUT -p UDP --dport domain ! -i ${LAN} -j REJECT
iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT

iptables -A INPUT -p TCP --dport 2222 -i ${WAN} -j ACCEPT

iptables -A INPUT -p TCP ! -i ${LAN} -d 0/0 --dport 0:1023 -j DROP
iptables -A INPUT -p UDP ! -i ${LAN} -d 0/0 --dport 0:1023 -j DROP

iptables -A FORWARD -i ${LAN} -s 192.168.2.0/255.255.255.0 -j ACCEPT
iptables -A FORWARD -i ${WAN} -d 192.168.2.0/255.255.255.0 -j ACCEPT
iptables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -t nat -A POSTROUTING -o ${WAN} -j MASQUERADE


echo 1 > /proc/sys/net/ipv4/ip_forward
for f in /proc/sys/net/ipv4/conf/*/rp_filter ; do echo 1 > $f ; done

Con este script ya tenemos configurada la Raspberry para funcionar como un router. Ahora solo tenemos que conectar los equipos al router 1 (tanto por wifi como por cable) y asignarles una dirección en la red 192.168.2.0/24 y la puerta de enlace 192.168.2.4.

Y ahora es cuando viene la parte divertida, es decir, la de resolver los problemas. El primero de ellos son las desconexiones de la wifi. Nuestra Raspberry es un cacharro poco potente que no lleva muy bien alimentar dispositivos por USB. La mía tiene conectado, además del adaptador wifi, una webcam. Aunque los tengo conectados mediante un hub USB con alimentación externa, la Raspberry no lo soporta bien y la wifi tiene desconexiones y en algunos casos de mucho tráfico puede fallar toda la red (incluida la ethernet). Como no tiene ningún dispositivo de entrada/salida conectado (ni teclado, ni ratón ni pantalla), la única forma de ver qué le pasa es entrando por ssh, lo cual no es posible si lo que nos está fallando es precisamente la red. Además está el pequeño detalle de que la conexión voy a usarla desde otro edificio y no me apetece desplazarme para reiniciar la raspberry. Así que tenemos que conseguir que nuestra Raspberry sea autónoma en este sentido, es decir, que se resuelva ella solita sus problemas. El problema de las desconexiones lo he resuelto con el siguiente script, como siempre escrito en ruby, mi lenguaje favorito.


Este script lo que hace es comprobar si hay conexión a internet intentando cargar google.es. También puede hacerse haciendo ping al gateway. Ambas formas están en el script (la del ping está comentada). Si no hay conexión a internet intenta reconectar a la wifi. Si después de diez intentos no lo consigue reinicia la Raspberry y envía un mail de notificación.  Antes de lanzarlo por primera vez tenemos que crear los ficheros  /usr/local/data/check_connection.info, que contiene la fecha y hora de la última comprobación, y /usr/local/data/count.txt, que contiene el número de reintentos fallidos de reconexión. Este último debemos crearlo con una sola línea cuyo valor tiene que ser 0. El script acepta los siguientes parámetros

check_connection.rb fichero_configuracion_mail mail TLS intervalo

El primer parámetro es el fichero de configuración de la cuenta de mail que usaremos para la notificación. Debe contener las siguientes líneas:

servidor_smtp
puerto_smtp
usuario_smtp
clave_smtp

El segundo parámetro es el destinatario del mail. El tercero es 0 ó 1 según vayamos a usar autenticación segura o no y el último es el número de segundos entre los reintentos de enviar el mail. Está programado para tres reintentos.

Con esto nuestra Raspberry es capaz de solucionarse las desconexiones. Pero el caso de la saturación de la red es curioso porque no se puede entrar por ssh, pero según parece sigue teniendo conexión a internet y en algunos casos incluso sigue ejerciendo de router correctamente (en otros no) con lo cual es complicado saber realmente cuándo está fallando de forma automática. Para resolverlo he programado otro script:


La idea es sencilla. Nos bajaremos periódicamente un fichero que contiene comandos que ejecutará la Raspberry. De esta forma accediendo al fichero con el móvil podremos añadir al fichero el comando sudo reboot que la reiniciará restaurándose así todo el sistema de red. El script necesita los ficheros /usr/local/data/remote_commands.info, que contiene la fecha y hora de la última ejecución de comandos. El formato de fecha es AAAAMMDDHHMMSS, como por ejemplo 20140206120010 para el 6 de febredo de 2014 a las 12:00:10, que es la fecha de mi última ejecución. Debemos crearlo con una fecha válida y anterior a hoy. El otro fichero es el fichero de comandos /usr/local/data/command.txt. Este fichero es el que nos descargaremos de internet (yo por ejemplo uso dropbox) y contiene la fecha en formato AAAAMMDDHHMMSS y el comando a ejecutar separados por un tabulador. Por ejemplo

20140206113100  lsusb
20140206113100  sudo reboot

El script compara la fecha de cada linea de este fichero con la de remote_commands.info y si es posterior ejecuta los comandos y envía el resultado por mail. El script acepta los siguientes parámetros

remote_commands.rb fichero_configuracion_mail mail TLS intervalo https://link_to_command.txt command.txt 

Los cuatro primeros parámetros son los mismos que en el script anterior y los dos últimos son el enlace al fichero de comandos y su nombre. El fichero de comandos será guardado en /usr/local/data.

Por último ambos scripts necesitan el fichero script_lib.rb en /usr/local/bin. Y con esto ya tenemos nuestro repetidor wifi autosuficiente DIY. Ahora solo nos queda lanzar los dos scripts periódicamente. Para ello modificaremos la el fichero /etc/crontab para añadirle estas dos líneas

0,15,30,45 *    * * *   Usuario /usr/local/bin/remote_commands.rb /usr/local/data/script/config mail@mail.com 1 15 https://link_to_command.txt command.txt
*  *    * * *   root    /usr/local/bin/check_connection.rb /usr/local/data/script/config mail@mail.com 1 15

La primera ejecuta los comandos remotos cada 15 minutos y la segunda comprueba la conexión cada minuto.