L'arbre de Nadal ciber

El nadal passat, tot passejant pel mercat de Santa Llucia de Barcelona vaig veure un d'aquests petits arbres 'artificials' atiborrats de fibra òptica, molt petit i que només costava 2.5€.

Arbre de nadal
 

Com que ja feia dies que portava de cap un parell de bestieses (com sempre vaja) i a la feina he sentit a dir que al nostre despatx hi falta ambient nadalenc, podriem penjar aquest petit arbre de nadal d'algun dels servidors que tenim aqui a la feina i controlar-lo en funció del trànsit que de xarxa que tingui no?
Va... som-hi! :D

Per començar dissecciono l'arbre, que porta un parell de bombetes petites a l'interior que il.luminen la fibra amb dos colors, crec que nosaltres ho podem millorar una mica no?

Base de l'arbre
Base de l'arbre

L'arbre pelat consisteix en aquesta estructura de fibra, que acaba 'ramificada' per diversos punts de l'abre. Aixi que en funció d'on apuntem la llum, ens arribarà a diferents branques de l'arbre.

Branques de fibra
 

El primer que caldria és canviar aquestes bombetes tant horteres per uns leds de colors, que sempre queden molt 'fashion'. Un de cada color primari, vermell, blau i verd i d'alta intensitat perque volem que se'ns vegi una mica bé l'arbre.
Per simplificar la feina, com que al lloc de treball hi tinc un PC, no posaré electrònica de control al mateix arbre i ho deixaré tot a l'estació de treball.
Com que hi ha una pressa relativa per posar-ho en funcionament, connectaré els leds al port paral.lel del PC amb una petita resistència de 330ohms i anem per feina.

Esquema arbre
 

D'acord amb el pinout del port paralel, connectarem una resistència seguida d'un led a la pota 2 del port paralel, una altra a la pota 3 i una altra a la pota 4. L'altre extrem dels leds el connectarem a qualsevol masa de la pota 18 a la 25.

Aixi puc controlar els leds des del meu linux amb un petit programet en C:

#include <stdio.h>
#include <sys/io.h>

#define BASE 0x378
main(int argc, char **argv)
{
int valor;
valor=atoi(argv[1]);
ioperm(BASE,8,1);
outb(valor,BASE);
}

que escriurà el valor que li passi per la linia de comandes al port paral.lel. Aixi amb un 1 encendrà el primer led, amb un 2 el segon, amb un 4 el tercer, amb un 7 tots o amb un 0 cap d'ells.
A veure que passa amb:

./disparaled 7
Leds arbre
Oh!

Ara que això ja ho tinc resolt, per anar bé hauria d'aconseguir fer-lo funcionar a partir d'events que passaran en un servidor i no en la meva màquina de treball, aixi quedarà una mica més espectacular.
Amb un fragment de codi que utilitzant la llibreria libpcap, permetrà utilitzar expressions molt flexibles a l'hora de decidir quin tràfic utilitzar per controlar el nostre arbre.
Per exemple, suposem que tenim un servidor web a la IP 192.168.2.100 i la meva estació de treball a 192.168.1.11.
Aquest programa a partir d'una expressió que li passaré per la linia de comandes enviarà un avis per xarxa a la meva estació de treball quan l'expressió que li he donat es compleixi. L'avis l'enviaré en forma d'un paquet UDP per un port determinat. Per l'exemple que vindrà ara faig servir el port 666.

El codi quedaria aixi:

#include <pcap.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/if_ether.h>
#include <sys/io.h>

int sock;
struct sockaddr_in client,server;
int structlength;
struct in_addr *addr;
char *dades="AA";

/* Callback que es crida cada vegada que ens arriba un paquet */
void my_callback(u_char *useless,const struct pcap_pkthdr* pkthdr,const u_char*
packet)
{
/* Aqui hem de llençar un paquet amb les caracteristiques que volguem */
if (sendto(sock,&dades,sizeof(dades),0,(struct sockaddr *) &server,sizeof(server)) < 0 )
{
perror("Problema al enviar\n");
exit(1);
}

}

int main(int argc,char **argv)
{
int i;
char *dev;
char errbuf[PCAP_ERRBUF_SIZE];
pcap_t* descr;
const u_char *packet;
struct pcap_pkthdr hdr; /* pcap.h */
struct ether_header *eptr; /* net/ethernet.h */
struct bpf_program fp; /* hold compiled program */
bpf_u_int32 maskp; /* subnet mask */
bpf_u_int32 netp; /* ip */

if(argc != 2){ fprintf(stdout,"Usage: %s \"filter program\"\n"
,argv[0]);return 0;}
/* Busquem un dispositiu per entrtenir-nos... */
dev = pcap_lookupdev(errbuf);
if(dev == NULL)
{ fprintf(stderr,"%s\n",errbuf); exit(1); }

/* Capturem parametres per el filtre */
pcap_lookupnet(dev,&netp,&maskp,errbuf);

/* Obrim el dispositiu per llegir paquets en mode promiscu */
descr = pcap_open_live("br0",BUFSIZ,1,-1,errbuf);
if(descr == NULL)
{ printf("pcap_open_live(): %s\n",errbuf); exit(1); }

/* Compilem l'expressio que se'ns ha passat desde la linia de comandes */
if(pcap_compile(descr,&fp,argv[1],0,netp) == -1)
{ fprintf(stderr,"L'expressio es incorrecte\n"); exit(1); }

/* Definim el filtre si la compilacio ha funcionat */
if(pcap_setfilter(descr,&fp) == -1)
{ fprintf(stderr,"Problema al filtrar\n"); exit(1); }

/* Reservem sockets i altres conyes per enviar els paquets */
/* Reservem un socket per poder enviar */
sock = socket(AF_INET,SOCK_DGRAM,0);
if(sock<0)
{
perror("socket no disponible\n");
exit(1);
}

/* Apuntem cap a l'extrem remot */
memset((char *) &server, 0, sizeof(server));
server.sin_family = AF_INET;
server.sin_addr.s_addr = inet_addr("192.168.1.11");
server.sin_port = htons(666);

/* Emplenem les dades de la nostra banda */
memset((char *) &client, 0, sizeof(client));
client.sin_family = AF_INET;
client.sin_addr.s_addr = htonl(INADDR_ANY);
client.sin_port = htons(0);
if (bind(sock, (struct sockaddr *) &client, sizeof(client)) <0)
{
perror("bind no disponible\n");
exit(1);
}

/* ... nem de festa */
pcap_loop(descr,-1,my_callback,NULL);

return 0;
}

Per compilar aquest tros de codi que anomenarem expressio.c, caldrà indicar que utilitzi la llibreria pcap:

cc -o expressio -lpcap expressio.c

i executem:

./expressio "dst port 80 and dst host 192.168.2.100 and tcp[tcpflags] & tcp-syn !=0"

al servidor.
En aquest cas cada cop que l'expressió és compleixi enviarà un paquet UDP cap a la meva màquina, amb destí al port 666.
L'expressió de l'exemple evalua cada petició que arribi per establir una connexió al port 80 tcp, és a dir al port del servidor web, destinada a la IP del servidor web, i que porti el flag de SYN, indicant que la connexió comença (si no posessim aquest darrer, tota la transferència de dades ens provocaria events) de manera que cada visita a una web del servidor provocarà que s'envii un senyal cap a la meva màquina.

Ara compilant aquest codi sobre la meva màquina:

#include <pcap.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/if_ether.h>
#include <sys/io.h>

#define BASE 0x378

/* Callback que es crida cada vegada que ens arriba un paquet */
void my_callback(u_char *useless,const struct pcap_pkthdr* pkthdr,const u_char*
packet)
{
char anterior;
/* Aqui hem de llen?ar un paquet amb les caracteristiques que volguem */
anterior=inb(BASE);
outb(anterior | 0x1,BASE);
usleep(4500);
outb(anterior & 0xfe ,BASE);
}
int main(int argc,char **argv)
{
int i;
char *dev;
char errbuf[PCAP_ERRBUF_SIZE];
pcap_t* descr;
const u_char *packet;
struct pcap_pkthdr hdr; /* pcap.h */
struct ether_header *eptr; /* net/ethernet.h */
struct bpf_program fp; /* hold compiled program */
bpf_u_int32 maskp; /* subnet mask */
bpf_u_int32 netp; /* ip */

if(argc != 2){ fprintf(stdout,"Usage: %s \"filter program\"\n"
,argv[0]);return 0;}

/* Busquem un dispositiu per entrtenir-nos... */
dev = pcap_lookupdev(errbuf);
if(dev == NULL)
{ fprintf(stderr,"%s\n",errbuf); exit(1); }

/* Capturem parametres per el filtre */
pcap_lookupnet(dev,&netp,&maskp,errbuf);

/* Obrim el dispositiu per llegir paquets sense activar el mode promiscu */
descr = pcap_open_live("eth0",BUFSIZ,0,-1,errbuf);
if(descr == NULL)
{ printf("pcap_open_live(): %s\n",errbuf); exit(1); }

/* Compilem l'expressio que se'ns ha passat desde la linia de comandes */
if(pcap_compile(descr,&fp,argv[1],0,netp) == -1)
{ fprintf(stderr,"L'expressio es incorrecte\n"); exit(1); }

/* Definim el filtre si la compilacio ha funcionat */
if(pcap_setfilter(descr,&fp) == -1)
{ fprintf(stderr,"Problema al filtrar\n"); exit(1); }

/* Reservem adreces del port paralel */
ioperm(BASE,8,1);

/* ... nem de festa */
pcap_loop(descr,-1,my_callback,NULL);

return 0;
}

i executant-lo amb:

./ledvermell "udp and port 666"

cada cop que el servidor monitoritzat per el codi de servidor rebi una petició web, l'arbre de nadal il.luminarà el led del color corresponent durant uns instants.
Només cal repetir el mateix procés utilitzant un port diferent per enviar els avisos entre el servidor i l'equip que té l'arbre, i vigilar ports diferents al servidor per assignar a cadascun dels colors, i tenim un arbre de nadal completament freak, que s'encen al ritme dels nostres servidors.

Apa, ja podem anar per un altre...