Quirks del kernel Linux

Lo primero, ¿sabes lo que es un “quirk“?  Según el traductor, tiene varias acepciones como capricho o peculiaridad. Es esta última acepción de la que vamos a hablar.

Linux tiene código “quirk” (peculiaridades) para ciertos dispositivos concretos.

Esto se debe a que hay muchos dispositivos que aunque tienen funcionalidades en común, incorporan “peculiaridades” que deben ser tratadas para explotar al máximo el dispositivo. Un ejemplo claro son los teclados con teclas multimedia o esos joysticks enormes que se emplean en los simuladores de vuelo.

Os voy a poner un ejemplo con un dispositivo de interfaz-humana (HID) al que vamos a intercambiar la funcionalidad de dos de sus teclas.

Como sabéis, cada dispositivo USB tiene asociado un par de códigos que representan un código unívoco de producto (identificador de fabricantes e identificador de producto). Cuando un dispositivo HID USB se conecta al sistema, el kernel busca si tiene asociado algún quirk, y si procede, aplica el “remapeo” de eventos.

Un fragmento de hid-belkin.c para un teclado Belkin.


static int belkin_input_mapping(struct hid_device ∗ hdev, struct hid_input ∗ hi, struct hid_field ∗ field, struct hid_usage ∗ usage,unsigned long ∗ ∗ bit, int  max)
{
unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);

    if ((usage >hid & HID_USAGE_PAGE) != HID_UP_CONSUMER || !(quirks & BELKIN_WKBD))
          return 0;
    switch (usage >hid & HID_USAGE) {
          case 0x03a: belkin_map_key_clear(KEY_SOUND);
          case 0x03b: belkin_map_key_clear(KEY_CAMERA);
          case 0x03c: belkin_map_key_clear(KEY_DOCUMENTS);
          default:
          return 0;
    }
    return 1;
}

¿es fácil de entender no? El código “mapea” tres eventos distintos a los códigos de kernel correspondientes a las teclas KEY_SOUND, KEY_CAMERA y KEY_DOCUMENTS.
Esta función de “mapeo” se ejecuta después de que la función “probe” del driver sea positiva y se haya obtenido la tabla de usos del dispositivo USB HID.
Dicha función de “mapeado” se aplica para cada uno de los códigos que reporte el dispositivo. Si os fijáis, cuando se produce un intercambio el código de retorno es 0.
Se devuelve 1 si no hay alteración (como puede ser el caso de la tecla ‘a’).

Si tenéis curiosidad veréis en que en todo el kernel hay numerosos “quirks” a todos los niveles.

 

Ubuntu Edge, la era PC-in-phone a 32 millones de dolares de distancia

 

Ubuntu se ha lanzado a esto del crowdfunding para traer a un nuevo concepto de gadget a la vida: el “pcphone“.

Imagina llegar a tu puesto de trabajo con tu “pcphone”, conectarle teclado y pantalla y ponerte a trabajar con Ubuntu. Se trata pues de un smartphone hipervitaminado que es capaz de correr un sistema operativo de escritorio como Ubuntu si nada que envidiar a los PC convencionales.

Para convertir este sueño de Mark Shuttleworth en realidad, ha optado por hacer en crowdfunding en Indiegogo por valor de 32 m$ (casi nada).

De conseguirlo, además de “reinventar el teléfono“, habrán batido un récord de recaudación.

La realidad es que a día de hoy llevan unos 7 m$ y faltan unos 24 días… complicado lo tienen.

Esta experiencia servirá para saber si este concepto de Ubuntu tiene cabida en el mundo actual y a qué precio (parece que los $825 de su precio objetivo esta lejos de lo que el consumidor quiere/puede pagar).

Espero la mayor de las suertes para este proyecto pero francamente creo que lo tienen complicado: producto poco definido, necesidad de mucha financiación, dudas sobre si Ubuntu podrá hacerlo, …

En 24 días sabremos si habemus “Ubuntu Edge” o no.

[Tip] Montar partición que está dentro de un dump de disco

En el desarrollo de sw/fw embebido, es habitual hacer un diskdump del contenido de la memoria flash o disco duro una vez está listo para salir.

Con esta imagen hecha, es sencillo replicar dicha configuración software para producción de más equipos.

Puede ocurrir que sea necesario acceder a dichos ficheros para consulta, generación de una nueva versión, etc.

Con software para equipos embebidos suele resultar difícil o imposible acceder al rootfs desde el propio sistema.

Voy a explicar un procedimiento que uso para poder montar dichas particiones desde cualquier equipo con GNU/Linux a partir del “dumpeo” de disco creado mediante DD.
Básicamente lo que vamos a hacer es localizar donde es probable que empiece nuestra partición y luego crear un dispositivo loopback aplicando un offset donde posiblemente empiece dicha partición.

Para localizar la partición haremos uso de la utilidad scandrive de Stephen J. Frield. Esta utilidad busca la palabra mágica que representa un header de ext2 (0xEF53). Además, y esto es casi lo más importante, localiza un potencial inodo-root (vamos, el directorio raiz). Esto sirve para intentar idefintificar correctamente donde comienza nuestra partición. ¿Cómo? Miramos el offset donde se encuentra el header de ext2 justo anterior a la localización del directorio raiz (depende del tamaño del disco, es posible que nos encontremos con muchos “falsos positivos”).

He modificado la utilidad scandrive, para que nos de el posible offset real para hacer el montaje correcto. Dicha posición se calcula restando al offset donde se encuentra el header de partición, los 1024 bytes de offset donde se encuentra el superbloque.

Este seria el procedimiento:

1º) Ejecutar
            % scandrive /v <dumpfile>
  Os saldrá algo parecido a esto:

scandrive 1.01 - 2009-11-08 - http://unixwiz.net/tools/
I/O buffer: 256 sectors of 512 bytes
Device disk_dump_final is open
Loop 0: scanning sector 0...
Found ptable magic at sector 0
Found ptable magic at sector 63
Found ptable magic at sector 78372
Found ptable magic at sector 85352
Found ptable magic at sector 90764
Found ptable magic at sector 94740
Found ptable magic at sector 96924
Found ext2 magic at offset 50359296 (part in: 50358272, size 669973493)
Loop 806: scanning sector 206336...
Found ext2 magic at offset 131089408 (part in: 131088384, size 179172)     
---> Found ext2 root dir at sector 259800 ***
Found ext2 magic at offset 138658816 (part in: 138657792, size 179172)
Found ext2 magic at offset 138970112 (part in: 138969088, size 179172)
---> Found ext2 root dir at sector 271664 ***
Found ext2 magic at offset 139101184 (part in: 139100160, size 179172)
Found ext2 magic at offset 139183104 (part in: 139182080, size 179172)
Found ext2 magic at offset 139342848 (part in: 139341824, size 179172)
---> Found ext2 root dir at sector 272344 ***
Found ext2 magic at offset 139449344 (part in: 139448320, size 179172)
Found ext2 magic at offset 139510784 (part in: 139509760, size 179172)
Found ext2 magic at offset 139682816 (part in: 139681792, size 179172)
(recortado)
Found ext2 magic at offset 265306112 (part in: 265305088, size 179172)
Loop 2167: scanning sector 554752...
Found ptable magic at sector 842680
Found ptable magic at sector 862888
Loop 3558: scanning sector 910848...
Found ext2 magic at offset 533741568 (part in: 533740544, size 179172)
Found ptable magic at sector 1167464
Loop 4919: scanning sector 1259264...
Found ptable magic at sector 1328685
Found ptable magic at sector 1499456
Found ext2 magic at offset 802177024 (part in: 802176000, size 179172)
Found ptable magic at sector 1583125
Loop 6281: scanning sector 1607936...
Found ptable magic at sector 1634968
Loop 7673: scanning sector 1964288...
Got EOF on sector 1966080
Finished scanning.
 2º)  Localizar el primer “–> Found ext2 root dir” (Marcado en rojo en el ejemplo)
 3º)  Si hay una entrada ext2 header anterior, es muy probable que el inicio de partición se encuentre en -1024 bytes (En el ejemplo, seria un offset de 131088384)
 4º) Llamar a:
           % losetup -o<offset-dado-por-scandrive> /dev/loop0 <dumpfile>
 5º) Hacer el mount normal
           % mount /dev/loop0 /mnt
Aquí teneis “scandrive.cpp
Hasta otra!

Control LED con libusb

LedController for GNU/Linux

LedController for GNU/Linux

Hoy me ha llegado un chinagadget comprado en DealExtreme.

Se trata de un notificador de nuevos emails o eventos de mensajería.

Como era de esperar el driver/software sólo funciona para sistemas Windows pero me gustaría poder utilizar este “cachibache” en mi Ubuntu.

He supuesto que encender una bombilla vía USB no debía ser muy complicado y me he puesto manos a la obra.

Lo primero que tenia que averiguar era la configuración y el protocolo del driver para encender/apagar el LED a voluntad.

Para hacer esta mini ingeniería inversa he utilizado VirtualBox y Wireshark.

Wireshark es ampliamente usado como sistema de captura y análisis de paquetes de red. Ahora además nos permite “sniffar” el tráfico USB.

Con VirtualBox se crea una maquina virtual Windows donde instalamos los drivers oficiales del notificador.

Con nuestra máquina virtual corriendo, ponemos a escuchar a nuestro sniffer.

Es necesario saber en que bus se encuentra nuestro dispositivo. Ésto se soluciona con un simple lsusb.

Ahora le indicamos a WireShark que escuche en dicho bus y ejecutamos un escenario donde se encienda/apague el led.

El resultado es un gran fichero de captura con mucha morralla.

Analizando el tráfico se observa un incontable número de paquetes enviados por interrupción pero cuyo contenido es siempre el mismo. Desconozco su propósito (¿quizás un sistema rudimentario de heartbeat?).

Filtrando por endpoint, se ve que hay dos endpoints, 1 y 2 (además del EP de control 0).

El EP 1 es el que usa para transferencias por interrupción del mensaje repetitivo.

El EP 2 es el que se usa para el envio de comandos de encendido.

En concreto, después de jugar con la aplicación de notificación, detecte la siguiente trama para encender el led rojo:


0x02 0x04 0x04 0x04 0x04

Para apagarlo, se usa ésta otra:


0x00 0x04 0x04 0x04 0x04

Con el protocolo (a priori) detectado, decidí hacer el driver en espacio de usuario para GNU/Linux.

Para acometer ésto, opté por usar la librería de acceso a USB, libusb.

Esta librería permite el desarrollo rápido de drivers/aplicaciones USB en GNU/Linux (también está disponible para Windows). Para dispositivos sencillos como este se ajusta perfectamente. Si por el contrario queremos controlar un dispositivo con alta intesidad de E/S, es recomendable implementarlo mediante un módulo kernel.

La implementación es sencilla. Se pasa como parámetro el color del LED a encender (en la secuencia, 2 = rojo, 1 = azul, 2 = verde).

Lo relevante está en la línea 55. Aquí es donde se envía por interrupción, el comando por el EP 2.

También es interesante la línea 41. Aquí se descarga el manejador kernel de dispositivo en caso de que algún driver lo haya reclamado (en este caso el driver HID).


#include <stdio.h>
#include <libusb.h>
#include <errno.h>

#define VID 0x1294
#define PID 0x1320

static struct libusb_device_handle *devh = NULL;

int main(int argc,char** argv)
{
 int ret;
 unsigned char code = 0;
 if (argc != 2 )
 {
    printf("syntax: %s red | green | blue | off\n",argv[0]);
    return -1;
 }
 if ( strcmp(argv[1],"red") == 0 )
 {
     code = 2;
 }
 else if ( strcmp(argv[1],"green") == 0 )
 {
     code = 3;
 }
 else if ( strcmp(argv[1],"blue") == 0 )
 {
     code = 1;
 }
 libusb_init(NULL);
 devh = libusb_open_device_with_vid_pid(NULL, VID, PID);
 if (devh == NULL )
 {
	printf("not found\n");
        return -1;
 }
 if ( libusb_kernel_driver_active(devh,0) )
 {
	printf("detach from kernel\n");
        ret = libusb_detach_kernel_driver(devh,0);
        if (ret < 0 )
        {
		printf("can't detach\n");
                return -1;
        }
 }
 char data[5];
 data[0] = code;
 data[1] = 0x4;
 data[2] = 0x4;
 data[3] = 0x4;
 data[4] = 0x4;
 int dummy;
 ret =  libusb_interrupt_transfer(devh,0x2,data,5,&dummy,0);
 if ( ret < 0 )
 {
  perror("error");
 }
return 0;
}

Os dejo el fuente de programa, una versión compilada dinámicamente y otra estáticamente.
Fuente: ledcontroller.tar.gz

[Tip] Copiar ficheros usando nc (netcat)

Netcat (más conocido por nc) es sin duda una herramienta sencilla y poderosa. Permite establecer comunicaciones TCP/IP y tiene un tamaño bastante discreto. Suele estar incluido en los firmwares de nuestros periféricos debido a su versatilidad y poco tamaño.

Os voy a explicar como transferir archivos por red usando netcat.

Supongamos que tenemos el fichero “vmlinuz” que queremos transferir a un nuevo equipo (con IP 192.168.1.2) .

Lo primero es poner el equipo receptor en modo escucha para recepcionar el fichero:


nc -l -p 10000 > vmlinuz

Los parámetros son sencillos. Con -l ponemos a nc en modo escucha y con -p especificamos el puerto.  Usamos redirección para indicar donde almacenar el fichero (vmlinuz).

Vamos a enviar el fichero


nc 192.168.1.2 10000 < vmlinuz

Poco que explicar: IP, puerto de destino y fichero de entrada (vmlinuz).

Espero que os sirva.

ACTUALIZACIÓN: El otro día no me funciono este tip usando clientes distintos de nc, tuve que añadir al emisor el flag “-q 0” (nc -q 0 192.168.1.2 10000 < vmlinuz) para que cerrara la transferencia justo al terminar el envío.

 

[Tip] Python: imprimir el traceback

Cuando programamos es habitual encontrarnos con errores que son difíciles de depurar si no tenemos información sobre lo que ha pasado.

En Python, lo normal es que de forma automática aparezca el traceback cuando se produce una excepción:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'ls' is not defined

Hay ocasiones en los que obtener esta información no es tan trivial.
Un ejemplo común es el desarrollo de servicios webs. En este caso no sirve de nada imprimir por pantalla el error, ya que no queda registrado en ningún sitio.

Para poder obtener el traceback y depurar correctamente el servicio, podemos hacer uso del módulo traceback.

El siguiente codesnippet permite imprimir en el archivo de log el correspondiente traceback:


try:

# Código bajo sospecha

....
except:

import traceback
import StringIO
s=StringIO.StringIO()
traceback.print_exc(file=s)
s.seek(0)
msg=s.read()
logging.debug("TRACEBACK: %s" % msg)

SSH: La navaja suiza del administrador de sistemas

Secure SHell (SSH) es un intérprete de ordenes remoto. Su principal uso es para la administración de sistemas remotos. Reemplaza al antiguo e inseguro “rlogin” mediante el uso de TLS (lo que añade cifrado de datos y evita el man-in-the-middle attack).

El uso habitual de SSH es:


ssh <usuario-remoto>@<host>

Si conecta con el equipo remoto, sólo nos basta introducir la contraseña y estaremos dentro.

Si sólo queremos ejecutar una orden o un script determinado, añadimos el comando al final. Un ejemplo habitual es reiniciar una máquina.


ssh root@server1.dominio.com reboot

Visto su uso habitual veamos otros ejemplos muy útiles para llevar mejor el día a día del administrador de sistemas.

Túnel proxy

Si no estamos muy seguros de que nuestra navegación web pase por un canal seguro (sin fisgones), podemos hacer un canal seguro entre nuestro equipo y nuestro servidor de confianza (si no teneis uno, podeis lanzar una micro-instancia de Amazon EC2, tendréis un buen proxy server por $0,02 por hora).

El tunel se establece así:


ssh  -D 8080 <user>@<host>

La opción -D 8080, indica que queremos hacer un túnel asociado al puerto local 8080. Sólo nos quedaría configurar nuestro navegador web para usar como proxy SOCKS el equipo “localhost” en el puerto 8080.

SSH con interfaz gráfica

Si no os sentís cómodos con la línea de comandos, o bien necesitáis correr una aplicación gráfica, SSH tambíen tiene una opción para ayudaros. Sólo necesitais estar ejecutando un servidor X Window (normalmente Xorg Server) en vuestra máquina y redireccionar el tráfico X11 hacia ella.


ssh  -Y <user>@<host>

El argumento “-Y” hará todo el trabajo de redirección por nosotros. Sólo nos queda ejecutar nuestra aplicación gráfica (Ej. netbeans).

Tunel proxy estático

Es una variante del túnel anterior pero con la particularidad de poder especificar el puerto de destino en el host remoto. Se ve mejor con un ejemplo.

Suponed que habéis desarrollado una aplicación servidor que recibe un mensaje y lo redistribuye vía Twitter a una cuenta especificada (vale cualquier otra aplicación imaginable). Esta aplicación escucha los mensajes en el puerto 1234.  Como ibais justo de tiempo (algo demasiado habitual) habéis optado por no usar TLS para cifrar la comunicación y utilizar en su lugar un canal SSH.

La solución es sencilla. El cliente tiene que asociar un puerto del equipo local al puerto de aplicación del servidor mediante la siguiente sintaxis.


ssh  -L <puerto-local>:<host-server>:1234 <user>:<remote-server>

Hay que especificar que <host-server> no tiene porqué ser igual a <remote-server>. Si tanto el equipo donde corre el servidor SSH como el que corre la aplicación servidor, son el mismo, la sintaxis sería:


ssh  -L <puerto-local>:localhost:1234 <user>:<remote-server>

Si no coinciden lo que ocurre es que habría una comunicación no segura entre <remote-server> y <host-server>.

Bueno, estas son algunas de las utilidades más importantes de SSH. Existen otras como “scp” (para copiar archivos) o “sftp” (ssh ftp).

Sólo deciros que si sois usuarios de Windows, podéis usar PuTTY.

Translate to:English
MenefanteMenéame TwitterTwitter