Home Corsi Programmazione Linux I Socket Filter di Linux

In evidenza

SMAU a Bologna con Research to Business

SMAU a Bologna con Research to Business

Il 6 e 7 giugno il Roadshow Smau Business torna a far tappa al Padiglione 33 della Fiera di Bologna e...
Gli articolisti del Portale

Gli articolisti del Portale

Abbiamo deciso di dare più visibilità a tutti coloro che hanno pubblicato articoli per noi, questo perché...
SMAU festeggia il suo quinto anno nel NordEst a Padova

SMAU festeggia il suo quinto anno nel NordEst a Padova

Il 18 e 19 aprile al padiglione 5 di Padova Fiere si terrà la prossima tappa di SMAU Business Roadshow. All'evento...
SMAU risale l’Italia e arriva a Roma

SMAU risale l’Italia e arriva a Roma

Il 21 e 22 marzo nella Nuova Fiera di Roma si terrà la prossima tappa di SMAU Business Roadshow. L'evento...

Bandi di Concorso

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

Esperto di servizi e tecnologie cloud al Digit PA

Procedura comparativa per il conferimento di un incarico di collaborazione coordinata e continuativa per un profilo senior di “Esperto di servizi e tecnologie cloud”, nell’ambito del progetto europeo “Open Dai” presso l' Ente nazionale per la digitalizzazione della Pubblica Amministrazione...

18 Apr 2012 - Letture:34

Selezione per addetto servizi informatici all'Autorità Portuale di Brindisi

Avviso di selezione pubblica per titoli e prova orale per l’assunzione di n. 1 “addetto ai servizi informatici”, a tempo pieno ed determinato triennale, previo periodo di prova, da inquadrare nell’Area Amministrativa. (Inquadramento al III Livello del C.C.N.L. dei Lavoratori...

12 Feb 2012 - Letture:435

Concorso pubblico per informatico alla Corte dei Conti

E' indetto un concorso pubblico, per   esami,   per   il reclutamento di tre unità di informatici da inquadrare nella III area, fascia retributiva F1, da destinare agli Uffici della Corte dei Conti con sede in Roma. Requisiti: laurea triennale in scienze e...

05 Feb 2012 - Letture:306

Istruttore informatico L.68/1999 al Comune di Potenza

Concorso pubblico, per esami, per la copertura di n. 2 posti di Istruttore Informatico, categoria C, posizione economica C1, a tempo pieno e indeterminato, con riserva assoluta alle categorie dilavoratori di cui all'art. 1 della legge n. 68/1999 (norme per...

21 Gen 2012 - Letture:244

Senior Business Consultant presso Lombardia Informatica

Selezione per figura Senior Business Consultant presso Lombardia Informatica. Il collaboratore dovrà fornire consulenza strategica e di business sulle tematiche verticali della Socio-Sanità. Requisiti di ammissione: Laurea in Ingegneria, Economia e Commercio o Scienze dell’Informazione; Percorsi di formazione professionale in ambito sanitario e socio-sanitario, su...

11 Gen 2012 - Letture:284

Bando progetto TAG (Toscana Area Giovani) per giovani talenti digitali

UPI Toscana ha indetto un avviso pubblico per la selezione di 20 giovani (2 per ciascuna delle 10 province della Toscana), da impiegare come formatori all'interno del Progetto TAG (Toscana Area Giovani). Il bando è finalizzato a valorizzare al meglio i...

03 Gen 2012 - Letture:271

Avviso pubblico per giovani dottori di ricerca per Uffici di Gabinetto

Avviso pubblico per il conferimento di n. 6 incarichi presso gli Uffici di diretta collaborazione del Ministro dell'istruzione dell'università e della ricerca, per soggetti estranei alla pubblica amministrazione. Ambiti di esperienza e Aree di competenza: Nuovi Media: nuove forma di comunicazione tra...

02 Gen 2012 - Letture:217

I più attivi

Dati e punteggi dell'ultimo anno

Giovanna Casamassima Giovanna Casamassima
46 articoli
15,989 punti totali
Rodolfo Giometti Rodolfo Giometti
49 articoli
5,061 punti totali
Vittorio D'Aversa Vittorio D'Aversa
33 articoli
3,425 punti totali
Fabio Bronzini Fabio Bronzini
9 articoli
0,318 punti totali
Fulvio Lucchetti Fulvio Lucchetti
9 articoli
0,275 punti totali

Ci hanno visitato

Oggi:1445
Ieri:2498
Totali (14/04/09):1162292

I nostri numeri

Articoli pubblicati: 552
Iscritti al portale: 766
Iscritti all'Elenco: 184
Iscritti ML Discussioni: 351
Iscritti ML Articoli: 30
Iscritti ML Lavoro: 166
 
I Socket Filter di Linux Stampa E-mail
(0 voti, media 0 di 5)
Area Corsi - Programmazione Linux
Scritto da Rodolfo Giometti   
Martedì 20 Luglio 2010 08:54
Articolo letto 1483 volte

Una interessante caratteristica introdotta oramai già dalle prime releadse di Linux è quella di poter filtrare i pacchetti di rete che raggiungono un sistema Linux secondo determinati parametri. E' possibile cioè, non solo fare in modo che un processo possa ricevere tutti i pacchetti che raggiungono una scheda di rete, ma è anche possibile filtrare tale flusso dati secondo regole ben determinate.

Una prima domanda, lecita, che ci potremmo fare su quanto appena detto potrebbe essere: «ok, ma questo lo si potrebbe fare anche all'interno del processo elaborando tutti i pacchetti ricevuti, non occorre una specifica funzionalità di nucleo!». In realtà questo ragionamento è fallace  per semplici ragioni di performance, infatti il numero di pacchetti di rete che il sistema può ricevere nell'unità di tempo è, spesso, molto alto e un filtro a livello utente è poco performante a causa dei molti cambi di contesto che si renderebbero necessari.

Ecco perché all'interno di Linux sono stati aggiunti i Socket Filter!

Cosa sono

In poche parole i Socket Filter non sono altro che dei filtri che permettono di scegliere, da parte di un processo, quali sono i pacchetti di rete che vuole riceve. In questo modo il processo si concentra solo sui pacchetti a cui è interessato demandando al kernal la scelta di cosa far passare o meno.

I Socket Filter lavorano a livello di Protocol Family, cioè lo strato software appena sopra i device driver delle schede di rete e sono implementati come una macchina a stati che esegue un dato programma (il filtro). La macchina a stati è programmata con un codice pseudo-assembler chiamato BPF (Berkeley packet filter).

Cenni al Berkeley packet filter

Il linguaggio BPF è molto potente e leggero, permette cioè di eseguire in maniera molto veloce il filtraggio dei pacchetti. Il programma tcpdump è sostanzialmente basato su questo linguaggio e il suo utilizzo ne dà un esempio pratico di come sia efficiente e largamente utilizzato.

Ma la cosa interessante del linguaggio BPF, e che lo rende ancora più attraente per chi deve analizzare particolari classi di pacchetti di rete, è che non occorre conoscerlo per utilizzarlo! Infatti all'interno del comando tcpdump è presente una specie di traduttore tra la sintassi delle espresisoni tcpdump e il suo equivalente in BPF! Se cioè chiediamo a tcpdump di mostrarci il codice BPF che implementa un particolare filtro lui lo fa subito!

# tcpdump -d host 192.168.32.37 and port 80
(000) ldh      [12]
(001) jeq      #0x800           jt 2    jf 18
(002) ld       [26]
(003) jeq      #0xc0a82025      jt 6    jf 4
(004) ld       [30]
(005) jeq      #0xc0a82025      jt 6    jf 18
(006) ldb      [23]
(007) jeq      #0x84            jt 10    jf 8
(008) jeq      #0x6             jt 10    jf 9
(009) jeq      #0x11            jt 10    jf 18
(010) ldh      [20]
(011) jset     #0x1fff          jt 18    jf 12
(012) ldxb     4*([14]&0xf)
(013) ldh      [x + 14]
(014) jeq      #0x50            jt 17    jf 15
(015) ldh      [x + 16]
(016) jeq      #0x50            jt 17    jf 18
(017) ret      #96
(018) ret      #0

Nell'esempio ho chiesto a tcpdump di filtrare tutti i pacchetti da/per l'host 192.168.32.37 sulla porta 80. L'interpretazione del meta linguagigo è abbastanza facile (sebbene non banale per chi non mastichi un po' di assembler) e ci mostra come sia facile generare il codice da dare in pasto al sistema per filtrare i pacchetti di rete che ci interessano.

Come si implementano

A questo punto però come si dà questo codice in pasto al sistema? La risposta è ancora più semplice, si usa l'opzione -dd.

Con tale opzione si dice infatti a tcpdump che quello che ci interessa non è il codice in assembler, ma la sua verisone già compilata e pronta da essere inserita in un programma C. Si ha cioè:

# tcpdump -dd host 192.168.32.37 and port 80
{ 0x28, 0, 0, 0x0000000c },
{ 0x15, 0, 16, 0x00000800 },
{ 0x20, 0, 0, 0x0000001a },
{ 0x15, 2, 0, 0xc0a82025 },
{ 0x20, 0, 0, 0x0000001e },
{ 0x15, 0, 12, 0xc0a82025 },
{ 0x30, 0, 0, 0x00000017 },
{ 0x15, 2, 0, 0x00000084 },
{ 0x15, 1, 0, 0x00000006 },
{ 0x15, 0, 8, 0x00000011 },
{ 0x28, 0, 0, 0x00000014 },
{ 0x45, 6, 0, 0x00001fff },
{ 0xb1, 0, 0, 0x0000000e },
{ 0x48, 0, 0, 0x0000000e },
{ 0x15, 2, 0, 0x00000050 },
{ 0x48, 0, 0, 0x00000010 },
{ 0x15, 0, 1, 0x00000050 },
{ 0x6, 0, 0, 0x00000060 },
{ 0x6, 0, 0, 0x00000000 },

Questa volta l'output di tcpdump può essere preso paro paro e copiato in un file C all'interno di una vettore di strutture struct sock_filter come segue:

struct sock_filter BPF_code[] = {
        { 0x28, 0, 0, 0x0000000c },
        { 0x15, 0, 16, 0x00000800 },
        { 0x20, 0, 0, 0x0000001a },
        { 0x15, 2, 0, 0xc0a82025 },
        { 0x20, 0, 0, 0x0000001e },
        { 0x15, 0, 12, 0xc0a82025 },
        { 0x30, 0, 0, 0x00000017 },
        { 0x15, 2, 0, 0x00000084 },
        { 0x15, 1, 0, 0x00000006 },
        { 0x15, 0, 8, 0x00000011 },
        { 0x28, 0, 0, 0x00000014 },
        { 0x45, 6, 0, 0x00001fff },
        { 0xb1, 0, 0, 0x0000000e },
        { 0x48, 0, 0, 0x0000000e },
        { 0x15, 2, 0, 0x00000050 },
        { 0x48, 0, 0, 0x00000010 },
        { 0x15, 0, 1, 0x00000050 },
        { 0x6, 0, 0, 0x00000060 },
        { 0x6, 0, 0, 0x00000000 },
};

Nel vettore è allora presente il programma che implementa il filto definito sopra; va quindi usata la chiamata di sistema setsockopt() per darlo in pasto al sistema:

filter.len = ARRAY_SIZE(BPF_code);
filter.filter = BPF_code;
setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter));

Ma facciamo un esempi pratico: supponiamo di voler catturare tutti i pacchetti di ping (icmp-echo e icmp-echoreply) per/da la nostra macchina.

Prima di tutto utilizziamo un programmino che, oltre ad implementare il nostro filtro, si preoccupa anche di dire al sistema che vogliamo ricevere tutti i pacchetti ethernet. Per fare questo dobbiamo utilizzare il protocollo (o, meglio, il domain) PF_PACKET.

Dalle pagine di man della chiamata di sistema socket() si legge:

DESCRIPTION
socket()  creates  an endpoint for communication and returns a descrip-
tor.

The domain argument specifies a communication domain; this selects  the
protocol  family  which will be used for communication.  These families
are  defined  in  <sys/socket.h>.   The  currently  understood  formats
include:

Name                Purpose                          Man page
PF_UNIX, PF_LOCAL   Local communication              unix(7)
PF_INET             IPv4 Internet protocols          ip(7)
PF_INET6            IPv6 Internet protocols          ipv6(7)
PF_IPX              IPX - Novell protocols
PF_NETLINK          Kernel user interface device     netlink(7)
PF_X25              ITU-T X.25 / ISO-8208 protocol   x25(7)
PF_AX25             Amateur radio AX.25 protocol
PF_ATMPVC           Access to raw ATM PVCs
PF_APPLETALK        Appletalk                        ddp(7)
PF_PACKET           Low level packet interface       packet(7)

Quindi con il comando man 7 packet si ottiene:

DESCRIPTION
Packet  sockets  are  used to receive or send raw packets at the device
driver (OSI Layer 2) level.  They allow the user to implement  protocol
modules in user space on top of the physical layer.

The  socket_type  is either SOCK_RAW for raw packets including the link
level header or SOCK_DGRAM for  cooked  packets  with  the  link  level
header  removed.   The  link level header information is available in a
common format in a sockaddr_ll.  protocol is the  IEEE  802.3  protocol
number in network order.  See the <linux/if_ether.h> include file for a
list of allowed protocols.  When protocol is  set  to  htons(ETH_P_ALL)
then all protocols are received.  All incoming packets of that protocol
type will be passed to the packet socket before they are passed to  the
protocols implemented in the kernel.


Only  processes  with effective UID 0 or the CAP_NET_RAW capability may
open packet sockets.

SOCK_RAW packets are passed to and from the device driver  without  any
changes  in  the  packet data.  When receiving a packet, the address is
still parsed and passed in a standard  sockaddr_ll  address  structure.
When transmitting a packet, the user supplied buffer should contain the
physical layer header.  That packet is then queued  unmodified  to  the
network  driver  of  the  interface defined by the destination address.
Some device drivers always add other headers.  SOCK_RAW is  similar  to
but  not compatible with the obsolete PF_INET/SOCK_PACKET of Linux 2.0.


SOCK_DGRAM operates on a slightly higher level.  The physical header is
removed  before the packet is passed to the user.  Packets sent through
a SOCK_DGRAM packet socket get a suitable physical layer  header  based
on  the  information in the sockaddr_ll destination address before they
are queued.

Quindi apprendiamo che con il tipo SOCK_RAW si possono spedire/ricevere dei pacchetti di tipo raw dalle schede di rete. Per specificare i pacchetti di tipo Internet Protocol packet su ethernet basta specificare allora come protocol il valore htons(ETH_P_IP) (si veda il file linux/if_ether.h). Si ha cioè:

sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP));

Un possibile corpo del nostro programma risulta allora essere: 

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <linux/if_ether.h>
#include <linux/filter.h>

#define ARRAY_SIZE(arr)         (sizeof(arr) / sizeof((arr)[0]))

int main(int argc, char *argv[])
{
        int sock, n, ret;
        char buffer[2048];
        unsigned char *iph, *ethh;
        struct ifreq req;

        struct sock_filter BPF_code[] = {
                #include "filter.h"
        };
        struct sock_fprog prog;

        /* Create a RAW socket into PF_PACKET domain */
        sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP));
        if (sock <0) {
                perror("socket");
                exit(EXIT_FAILURE);
        }

        /* Set the network card in promiscuos mode */
        strncpy(req.ifr_name, "eth0", IFNAMSIZ);
        ret = ioctl(sock, SIOCGIFFLAGS, &req);
        if (ret < 0) {
                perror("ioctl");
                exit(EXIT_FAILURE);
        }
        req.ifr_flags |= IFF_PROMISC;
        ret = ioctl(sock, SIOCSIFFLAGS, &req);
        if (ret < 0) {
                perror("ioctl");
                exit(EXIT_FAILURE);
        }

        /* Attach the filter to the socket */
        prog.len = ARRAY_SIZE(BPF_code);
        prog.filter = BPF_code;
        ret = setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER,
                                &prog, sizeof(prog));
        if (ret < 0) {
                perror("setsockopt");
                exit(EXIT_FAILURE);
        }

        while (1) {
                printf("---\n");
                n = recvfrom(sock, buffer, 2048, 0, NULL, NULL);
                if (n < 0) {
                        perror("recvfrom");
                        exit(EXIT_FAILURE);
                }

                printf("got %d bytes\n",n);

                /* Check to see if the packet contains at least
                 * complete Ethernet (14), IP (20) and TCP/UDP
                 * (8) headers.
                 */
                if (n < (14 + 20 + 8)) {
                        fprintf(stderr, "incomplete packet\n");
                        exit(EXIT_FAILURE);
                }

                ethh = (unsigned char *) buffer;
                printf("Source MAC address: "
                        "%02x:%02x:%02x:%02x:%02x:%02x\n",
                        ethh[0],ethh[1],ethh[2], ethh[3],ethh[4],ethh[5]);
                printf("Destination MAC address: "
                        "%02x:%02x:%02x:%02x:%02x:%02x\n",
                        ethh[6],ethh[7],ethh[8], ethh[9],ethh[10],ethh[11]);

                iph = (unsigned char *) buffer + 14; /* skip eth header */
                if (*iph == 0x45) { /* double check for IPv4
                                     * and no options present */
                        printf("source host %d.%d.%d.%d\n",
                                iph[12],iph[13], iph[14],iph[15]);
                        printf("destination host %d.%d.%d.%d\n",
                                iph[16],iph[17], iph[18],iph[19]);
                        printf("source,Dest ports %d,%d\n",
                                (iph[20] << 8) + iph[21],
                                (iph[22] << 8) + iph[23]);
                        printf("layer-4 protocol %d\n", iph[9]);
                 }
         }

         return 0;
}

In questo programma, dopo aver creato un socket si passa all'impostazione del promiscuos mode per l'interfaccia eth0. Il promiscuos mode serve per catturare tutti i pacchetti che raggiungono la nostra interfaccia di rete, quindi non strettamente  necessario per il nostro problema specifico, ma l'ho voluto aggiungere egualmente per  rendere il nostro programma ancora più versatile.

Impostato quindi il promiscuos mode (occorrono i privilegi di root per eseguirlo) si passa all'impostazione del filtro il cui codice viene automaticamente generato (sempre come utente root) con il comando make e con Makefile:

TARGETS = filter_packets
FILTER = host 192.168.32.37 and port 80

CFLAGS = -Wall -O2

all : $(TARGETS)

filter_packets.c : filter.h
filter.h :
        echo '/* tcpdump -dd $(FILTER) */' > $@
        tcpdump -dd $(FILTER) >> $@

clean :
        rm -rf $(TARGETS)
        rm -rf filter.h

Questo Makefile ci permette di cambiare al volo il filtro che vogliamo usare semplicemente ridefinendolo al momento della compilazione come segue:

# make FILTER='icmp[icmptype] = icmp-echo or icmp[icmptype] = icmp-echoreply'
echo '/* tcpdump -dd icmp[icmptype] = icmp-echo or icmp[icmptype] = icmp-echoreply */' > filter.h
tcpdump -dd icmp[icmptype] = icmp-echo or icmp[icmptype] = icmp-echoreply >> filter.h
cc -Wall -O2    filter_packets.c   -o filter_packets

Bene, una volta impostato il filtro ci si mette allora in attesa dei dati utilizzando la chiamata di sistema recvfrom(). Proviamo il programma; lanciamolo su di una macchina:

# ./filter_packets
---

il processo si sospende in attesa di appacchetti; poi facciamo ping verso questa macchina da un altro host, otterremo che il processo si risveglia ed inizia a ricevere pacchetti:

got 96 bytes
Source MAC address: 00:23:54:5f:15:78
Destination MAC address: 00:18:f3:06:7d:bd
source host 192.168.32.254
destination host 192.168.32.37
source,Dest ports 2048,47055
layer-4 protocol 1
---
got 96 bytes
Source MAC address: 00:23:54:5f:15:78
Destination MAC address: 00:18:f3:06:7d:bd
source host 192.168.32.254
destination host 192.168.32.37
source,Dest ports 2048,11208
layer-4 protocol 1
---
got 96 bytes
Source MAC address: 00:23:54:5f:15:78
Destination MAC address: 00:18:f3:06:7d:bd
source host 192.168.32.254
destination host 192.168.32.37
source,Dest ports 2048,34999
layer-4 protocol 1
---
got 96 bytes
Source MAC address: 00:23:54:5f:15:78
Destination MAC address: 00:18:f3:06:7d:bd
source host 192.168.32.254
destination host 192.168.32.37
source,Dest ports 2048,49843
layer-4 protocol 1
---
got 96 bytes
Source MAC address: 00:23:54:5f:15:78
Destination MAC address: 00:18:f3:06:7d:bd
source host 192.168.32.254
destination host 192.168.32.37
source,Dest ports 2048,53925
layer-4 protocol 1
---

Da notare che se proviamo a fare una qualsiasi altra attività di rete verso l'host su cui gira il nostro filtro non vedremo alcunché in output.

 


Questa dispensa del corso Programmazione Linux è opera di Rodolfo Giometti (Copyright © 2010) ed è rilasciata dall'autore «as is» (così com'è) e distribuita sotto licenza Creative Commons Attribuzione – Condividi allo stesso modo 2.5 Italia.

 
 

In primo piano

Simons Voss: un mondo senza chiavi

Simons Voss: un mondo senza chiavi

Il sistema di gestione e di controllo degli accessi 3060 si presenta come un’alternativa con enormi vantaggi...
Pubblicità mirata dei prodotti

Pubblicità mirata dei prodotti

Hai un prodotto o un servizio da pubblicizzare? Fallo su consulenti-ict.it! Pubblicizzare un prodotto...
Recensioni sul Portale

Recensioni sul Portale

Hai un prodotto hardware/software o un libro riguardante uno dei temi dell'ICT? Vuoi farlo conoscere...

Offerte di lavoro

Visualizza Topic »

Eventi

Non ci sono eventi in programma
Maggio 2012
D L M M G V S
29 30 1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31 1 2
Giugno 2012
D L M M G V S
27 28 29 30 31 1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30

Sondaggi

busyCaricamento Sondaggio...