Next Previous Contents

2. Parte2 Descrizione delle socket Raw

In questa sezione vengono descritte le socket raw ovvero delle system call presenti nei sistemi Unix che permettono di lavorare al livello rete dello stack ISO/OSI, verranno descritti i principali file header comprese le strutture per poter gestire i pacchetti a questo livello.

2.1 Descrizione degli header principali usati nelle socket raw

In questa sezione verranno descritti gli header principali che vengono coinvolti nell'utilizzo delle socket raw e indispensabili per la comunicazione nell'ambito di reti TCP/IP. Sono descritti nell'ordine header ip, header tcp, e infine l'header udp.


 Struttura header IP ( /usr/include/netinet/ip.h ) 
 ....... 
 struct iphdr 
 { 
 #if __BYTE_ORDER == __LITTLE_ENDIAN
 u_int8_t ihl:4; /* lunghezza preambolo 4 bit*/
 u_int8_t version:4; /* versione 4 bit */
 #else __BYTE_ORDER == __BIG_ENDIAN
 u_int8_t version:4; /* versione 4 bit */
 u_int8_t ihl:4; /* lunghezza preambolo 4 bit */
 #endif
 u_int8_t tos; /* tipo di servizio 8 bit */
 u_int16_t tot_len; /* lunghezza totale 16 bit */
 u_int16_t id; /* identificazione 16 bit */
 u_int16_t frag_off; /* offset di frammentazione 16 bit*/
 u_int8_t ttl; /* tempo di vita del pacchetto 8 bit */
 u_int8_t protocol; /* protocollo di livello trasporto 8 bit */
 u_int16_t check; /* checksum 16 bit */
 u_int32_t saddr; /* indirizzo sorgente 32 bit */
 u_int32_t daddr; /* indirizzo destinazione 32 bit */
 /* Le opzioni iniziano qui */
 };
 struct ip_options
 {
 u_int32_t faddr; /* indirizzo del primo router */
 u_int8_t optlen;
 u_int8_t srr;
 u_int8_t rr;
 u_int8_t ts;
 u_int8_t is_setbyuser:1;
 u_int8_t is_data:1;
 u_int8_t is_strictroute:1; /* opzione di source routing */
 u_int8_t srr_is_hit:1; 
 u_int8_t is_changed:1; /* IP checksum non è valido */
 u_int8_t rr_needaddr:1; /* record route */
 u_int8_t ts_needtime:1; /* ogni router registra un timestamp al pacchetto (debug
 )*/
 u_int8_t ts_needaddr:1; /* record route di timestamp */
 u_int8_t router_alert;
 u_int8_t __pad1;
 u_int8_t __pad2;
 #ifdef __GNUC__
 u_int8_t __data[0];
 #endif
 };
 .......

La struttura iphdr rappresenta l'header IP di un pacchetto, innanzitutto si può osservare la distinzione tra LITTLE_ENDIAN e BIG_ENDIAN che distinguono il modo in cui viene trasmesso il pacchetto, BIG_ENDIAN da sinistra a destra, con il bit piu significativo del campo Version ; nelle macchine LITTLE_ENDIAN invece è necessaria una conversione software sia in trasmissione che in ricezione (Lo SPARC è di tipo big endian, mentre il PENTIUM è di tipo little endian). Passiamo ora a descrivere i vari campi dell'header IP:

VERSION:
Descrive la versione del protocollo utilizzato dal datagram ipv4 (4 bit).

LUNGHEZZA DELL'HEADER:
Siccome la lunghezza dell'header non è constante in questo campo è contenuta la lunghezza dell'header in parole di 32 bit il valore minimo è 5 (nessuna opzione header=20 byte) il massimo è 15. (header uguale a 60 byte) (4 bit).

TIPO DI SERVIZIO:
Consente agli host di comunicare alla rete il tipo di servizio desiderato in base ai parametri: ritardo, capacità di trasmissione affidabilità (8 bit).

LUNGHEZZA DEL PACCHETTO:
Riguarda l'intero pacchetto la massima lunghezza è di 65.536 byte(16 bit).

IDENTIFICAZIONE:
È necessario per permettere all'host di destinazione di determinare a quale datagram appartiene un frammento appena arrivato.Tutti i frammenti di un datagram contengono lo stesso numero di identificazione (16 bit).

OFFSET DI FRAMMENTAZIONE DEL PACCHETTO:
Indica in quale posizione del datagram corrente si trova questo frammento, tutti i frammenti tranne l'ultimo devono essere multipli di 8 byte, il campo è di 13 bit , avrò quindi un massimo di 8192 frammenti (8192 x 8 = 65536 byte al massimo(IP packet))(13 bit).

TEMPO DI VITA DEL PACCHETTO:
È un contatore utilizzato per limitare il tempo di vita dei pacchetti sulla rete:256 ; quando il contatore raggiunge lo 0 il pacchetto viene scartato e viene inviato alla destinazione un messaggio di avvertimento (8 bit).

PROTOCOLLO:
Campo che indica il protocollo che viene usato al livello trasporto (TCP, UDP e altri) la numerazione dei protocolli è globale ed è descritta nel RFC 1700.

INDIRIZZO SORGENTE E DESTINAZIONE:
Sono rispettivamente gli indirizzi del mittente e del destinatario del pacchetto composti da 32 bit ognuno.

CHECKSUM:
Verifica solamente il preambolo IP, è utilizzato per verificare gli errori generati dai router.L'algoritmo consiste nel sommare tutte le parole di 16 bit che arrivano usando l'aritmetica di complemento a 1 e quindi prendendo il complemento a 1 del risultato. Il risultato finale deve essere 0. Da notare che l'algoritmo deve essere cambiato ad ogni salto perchè c'è almeno un campo che cambia il TTL(time to live).

La seconda struttura illustra le opzioni che si possono aggiungere a un pacchetto IP le piu usatesono descritte di seguito:

SECURITY:
Descrive il grado di segretezza delle informazioni, in teoria un router militare dovrebbe utilizzare questo campo per richiedere di non attraversare paesi considerati a rischio.

STRICT SOURCE ROUTING:
Fornisce il percorso completo da seguire.

LOOSE SOURCE ROUTING:
Richiede che il pacchetto attraversi una lista di router specificati nell'ordine specificato ma e' possibile che passi attraverso altri router lungo il percorso.

RECORD ROUTE:
Forza i router lungo il cammino ad aggiungere il loro indirizzo IP al campo opzione.

TIMESTAMP:
È simile all'opzione record route a parte il fatto che oltre a registare l'indirizzo a 32 bit ogni router regista un timestamp di 32 bit (motivi diagnostici).


struttura header TCP (/usr/include/netinet/tcp.h)

 struct tcphdr
 {
 u_int16_t source; // porta sorgente 16 bit
 u_int16_t dest; // porta destinazione 16 bit
 u_int32_t seq; // numero di sequenza 32 bit
 u_int32_t ack_seq; // numero di ack 32 bit
 #if __BYTE_ORDER == __LITTLE_ENDIAN
 u_int16_t res1:4; // 
 u_int16_t doff:4;
 u_int16_t fin:1; // flag FIN
 u_int16_t syn:1; // flag SYN
 u_int16_t rst:1; // flag RST
 u_int16_t psh:1; // flag PSH
 u_int16_t ack:1; // flag ACK
 u_int16_t urg:1; // flag URG
 u_int16_t res2:2; 
 #elif __BYTE_ORDER == __BIG_ENDIAN
 u_int16_t doff:4;
 u_int16_t res1:4;
 u_int16_t res2:2;
 u_int16_t urg:1;
 u_int16_t ack:1;
 u_int16_t psh:1;
 u_int16_t rst:1;
 u_int16_t syn:1;
 u_int16_t fin:1;
 #else
 #error "Adjust your defines"
 #endif
 u_int16_t window;
 u_int16_t check;
 u_int16_t urg_ptr;
 };

a struttura precedente rappresenta i vari campi che compongono l'header di un pacchetto TCP il preambolo fisso di 20 byte piu una parte opzionale. I vari campi sono descritti di seguito:

SORGENTE E DESTINAZIONE:

Rispettivamente porta sorgente e porta destinazione identificano gli estremi locali di una connessione, ogni host deve decidere come allocare le propie porte successive alla 256. Il punto di accesso del livello trasporto sarà quindi identificato dall'indirizzo formato ADDRESS IP:PORTA.

NUMERO DI SEQUENZA:
l numero di sequenza viene assegnato ad ogni pacchetti durante la connessione tra 2 host e composto da 32 bit.

NUMERO ACK:
È uguale a 1 per indicare che il numero di ack è valido, se ack=0 il segmento non contiene ack e il campo numero di ack viene ignorato.

PSH:
ndica dati di tipo push, in questo modo si richiede al mittente di consegnare i dati dell'applicazione al momento dell'arrivo, evitando che siano salvati in un buffer di attesa.

RST:
Viene utilizzato per reinizializzare una connessione che è diventata instabile a causa del guasto di un host o per qualche altro motivo. Viene anche utilizzato per rifiutare l'apertura di una connessione.

SYN:
Viene utilizzato per creare una connessione. la richiesta di connessione è caratterizzata da SYN=1 ACK=0 la risposta sara' SYN=1 ACK=1.

FIN:
Viene utilizzato per chiudere una connessione.Specifica il mittente non ha altri dati da spedire, tuttavia dopo aver chiuso una connessione, un processo può continuare a rivcevere dati indefinitivamente.

WINDOW:
Il controllo di flusso TCP è gestito utilizzando un protocollo slinding window a dimensione variabile. Il campo window specifica quanti byte possono essere spediti a partire dal byte confermato.

CHECKSUM:
Per garantire l'affidabilità è presente anche un campo checksum, che verifica il preambolo i dati e lo pseudopreambolo. Quando viene eseguito il calcolo, il campo checksum viene posto uguale a 0, e il campo dati viene completato con un 0 se la lunghezza è un numero dispari. L'algoritmo di checksum somma tutte le parole di 16 bit in complemento a 1 e quindi prende il complemento a 1 della somma. Come conseguenza quando il ricevente esegue il calcolo sull'intero segmento (compreso il checksum) il risultato deve essere 0.


struttura udp header (/usr/unclude/netinet/udp.h)

 struct udphdr {
 u_int16_t source;
 u_int16_t dest;
 u_int16_t len;
 u_int16_t check;
 };

Questa struttura rappresenta i campi dell'header di un pacchetto UDP, a differenza di un collegamento TCP dove viene instaurata una connessione inziale nella comunicazione UDP il pacchetto viene spedito ed è indipendente dagli altri.

SORGENTE E DESTINAZIONE:
Indirizzi sorgente e destinazione del host mittente e destinatario 32 bit ognuno.

LUNGHEZZA:
Lunghezza del preambolo udp.

CHECKSUM:
Campo checksum del preambolo udp.

2.2 Esempi in C commentati sull'utilizzo delle socket raw

In questa sezione verranno forniti esempi di programmi che fanno uso delle socket raw.

GENERATORI DI PACCHETTI TCP

 /*******************************************************************************************
 * Questo programma tcp.c genera un pacchetto tcp con il flag fin = 1 ( chiusura connessione
 )
 * il programma usa socket raw per la creazione del pacchetto tcp che viene infine inviato,
 * con la chiamata sendto().
 * n.b. Dalla linea di comando viene modificato il propio IP-number!!!
 * 
 * Il programma deve essere lanciato in questo:
 * tcp porta_sorgente indirizzo_sorgente porta_remota indirizzo_remoto
 *
 * Il programma viene compilato nel seguente modo:
 * gcc -o tcp tcp.c
 * 
 ******************************************************************************************/

 /* Dichiarazione degli header principali usati nel programma */

 #include <stdlib.h>
 #include <stdio.h>
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <netinet/in_systm.h>
 #include <netinet/ip.h>
 #include <netinet/tcp.h>
 #include <string.h>
 #include <unistd.h>
 #include <time.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <sys/types.h>

 /* Dichiarazione dei prototipi delle funzioni */

 void tcp_gen(char *packet,unsigned short sport,
 unsigned short dport,unsigned long seq,
 unsigned long ack);

 void udp_gen(char *packet,unsigned short sport,
 unsigned short dport,unsigned short length);

 void ip_gen(char *packet,unsigned char protocol,struct in_addr saddr,
 struct in_addr daddr,unsigned short length);

 unsigned short trans_check( unsigned char proto, char *packet,
 int length, struct in_addr source_address,
 struct in_addr dest_address);

 unsigned short in_cksum(unsigned short *addr,int len);

 #define IPVERSION 4 /* versione del pacchetto IP */
 #define DEFAULT_TTL 60 /* Definizione del tempo di vita dei pacchetti */
 #define TH_OFFSET 5
 #define TCP_WINDOW_SIZE 512 /* larghezza della finestra del tcp */

 /* struttura per il calcolo del checksum sul pacchetto tcp*/

 struct psuedohdr {
 struct in_addr source_address;
 struct in_addr dest_address;
 unsigned char place_holder;
 unsigned char protocol;
 unsigned short length;
 } psuedohdr;

 /*********************************************************************************/
 /*********************** calcolo del checksum sul tcp header *********************/
 /*********************************************************************************/

 unsigned short trans_check(unsigned char proto,
 char *packet,
 int length,
 struct in_addr source_address,
 struct in_addr dest_address)
 {
 char *psuedo_packet;
 unsigned short answer;

 /* compilazione della struttura pseudohdr tcp */
 psuedohdr.protocol = proto;
 psuedohdr.length = htons(length);
 psuedohdr.place_holder = 0;
 psuedohdr.source_address = source_address;
 psuedohdr.dest_address = dest_address;

 /* allocazione della struttura pseudohdr */
 if((psuedo_packet = malloc(sizeof(psuedohdr) + length)) == NULL) {
 perror("malloc");
 exit(1);
 }

 /* copia la struttura pseudohdr nella blocco puntato da pseudo_packet */
 memcpy(psuedo_packet,&psuedohdr,sizeof(psuedohdr));
 memcpy((psuedo_packet + sizeof(psuedohde,packet,length);

 /* applica l'algoritmo del checksum allo pseudo pacchetto*/
 answer = (unsigned short)in_cksum((unsigned short *)psuedo_packet,
 (length + sizeof(psuedohdr)));
 free( psuedo_packet ) ; 
 return answer;
 }

 /*********************************************************************************/
 /************************* algoritmo del checksum ********************************/
 /*********************************************************************************/

 unsigned short in_cksum(unsigned short *addr,int len)
 {
 register int sum = 0;
 u_short answer = 0;
 register u_short *w = addr;
 register int nleft = len;

 /*
 * Questo ciclo somma nel registro accumulatore (sum) 32 bit le parole di
 * 16 bit che vengono estratte dalla struttura addr.
 */
 while (nleft > 1) {
 sum += *w++;
 nleft -= 2;
 }

 /* se la lunghezza del campo dati è numero dispari viene completato */
 /* con un 0 addizionale .*/
 if (nleft == 1) {
 *( u_char * )( &answer ) = *( u_char * )w ;
 sum += answer;
 }

 /* viene calcolato il complemento a 1 della somma delle word */
 sum = (sum >> 16) + (sum & 0xffff); 
 sum += (sum >> 16);
 answer = ~sum; 
 return(answer);
 }

 /*********************************************************************************/
 /*********************** generatore pacchetto IP *********************************/
 /*********************************************************************************/

 void ip_gen(char *packet,unsigned char protocol,struct in_addr saddr,
 struct in_addr daddr,unsigned short length)
 {

 /* definisco un puntatore alla struttura */
 struct iphdr *iphdr;

 /* La variabile pacchetto diventa puntatore alla struttura iphdr */
 iphdr = (struct iphdr *)packet;
 memset((char *)iphdr,'\0',sizeof(struct iphdr));

 /* compilo la struttura ip */

 iphdr->ihl = 5; 
 iphdr->version = IPVERSION;
 /* BIG ENDIAN e LIITLE ENDIAN */
 #ifdef IP_LEN_HORDER
 iphdr->tot_len = length;
 #else
 iphdr->tot_len = htons(length);
 #endif /* IP_LEN_HORDER */

 iphdr->id = htons(getpid());
 iphdr->ttl = DEFAULT_TTL;
 iphdr->protocol = protocol;
 iphdr->saddr = saddr.s_addr;
 iphdr->daddr = daddr.s_addr; 

 /* calcola il checksum del pacchetto*/
 iphdr->check = (unsigned short)in_cksum((unsigned short *)iphdr,
 sizeof(struct iphdr));
 return;
 }

 /*********************************************************************************/
 /*************** dichiarazione del prototipo della funzione tcp_gen **************/
 /*********************************************************************************/

 void tcp_gen(char *packet,unsigned short sport,
 unsigned short dport,unsigned long seq,
 unsigned long ack)
 {
 /* puntatore alla struttura tcphdr */
 struct tcphdr *tcp;

 /* Cast di packet da (char *) a (struct tcphdr*) */
 tcp = (struct tcphdr *)packet;
 memset((char *)tcp,'\0',sizeof(struct tcphdr));

 /* compilazione della struttura tcp */

 tcp->source = htons(sport);
 tcp->dest = htons(dport);
 tcp->seq = htonl(seq);
 tcp->ack_seq = htonl(ack);
 tcp->res1 = 0;
 tcp->doff = TH_OFFSET;
 tcp->window = htons(TCP_WINDOW_SIZE);
 tcp->fin = 1;

 return;
 }
 /*********************************************************************************/
 /********************************** M A I N **************************************/
 /*********************************************************************************/

 int main(int argc,char *argv[])
 {
 /* vettore pacchetto di lunghezza ip header + tcp header */
 unsigned char packet[ sizeof(struct iphdr) + sizeof(struct tcphdr) ];

 /* sockaddr_in struttura della socket */
 struct sockaddr_in mysocket;

 /* porte sorgenti e destinazioni */
 unsigned short sport, dport;

 /* struttura per l'indirizzamento ID/RETE ID/HOST */
 struct in_addr saddr, daddr;

 /* puntatore alla struttura tcphdr */
 struct tcphdr *tcp;
 unsigned long seq, ack;
 int sockd, on = 1;

 /* Prelevo gli argomenti dalla riga di comando */
 if(argc < 5) {
 fprintf(stderr,"usare: %s porta_sorg. indirizzo_sorg. porta_dest. 
 indirizzo_dest\n", argv[0]);
 exit(1);
 }

 /* Compila la struttura per le socket con porta sorgente 
 e indirizzo sorgente */
 sport = (unsigned short)atoi(argv[1]);
 saddr.s_addr = inet_addr(argv[2]);

 /* Compila la struttura per le socket con porta destinazione e 
 indirizzo destinazione */
 dport = (unsigned short)atoi(argv[3]);
 daddr.s_addr = inet_addr(argv[4]);

 /* Creo il socket descriptor con l'opzione socket raw */
 if((sockd = socket(AF_INET,SOCK_RAW,IPPROTO_RAW)) < 0) {
 perror("socket");
 exit(1);
 }

 /* Setto alcune opzioni inerenti al livello del protocollo IP */
 if(setsockopt(sockd,IPPROTO_IP,IP_HDRINCL,(char *)&on,sizeof(on)) < 0) {
 perror("setsockopt");
 exit(1);
 }

 /* Genero numero di sequenza ed ack casuali */
 srand(getpid());
 seq = rand()%time( NULL );
 ack = rand()%time( NULL );

 /* Genero pacchetto IP */
 ip_gen(packet,IPPROTO_TCP,saddr,daddr,sizeof(packet));

 /* Sposto il puntatore della struttura tcp header della dimensione del */
 /* pacchetto + la struttura ip header */
 tcp = (struct tcphdr *)(packet + sizeof(struct iphdr));

 /* Genero il pacchetto TCP */
 tcp_gen((char *)tcp,sport,dport,seq,ack);

 /* checksum calcolato sul pacchetto IP */
 tcp->check = trans_check(IPPROTO_TCP,(char *)tcp,
 sizeof(struct tcphdr),
 saddr,
 daddr);

 /*inizializza la struttura mysocket */
 memset(&mysocket,'\0',sizeof(mysocket));

 /*Compilo la struttura mysocket */
 mysocket.sin_family = AF_INET;
 mysocket.sin_port = htons(dport);
 mysocket.sin_addr = daddr;

 /* Spedisco il pacchetto da me creato con il flag fin a a 1 
 secondo i parametri immessi da linea di comando.*/
 if(sendto(sockd,&packet,sizeof(packet),0x0,(struct sockaddr *)&mysocket,
 sizeof(mysocket)) != sizeof(packet)) {
 perror("sendto");
 exit(1);
 } 
 exit(0);
 }
 /**********************************************************************************/

DESCRIZIONE DEL PROGRAMMA TCP.C

Il programma tcp.c è formato da 3 principali routine : tcp_gen, ip_gen, e in_cksum.

Se si ha la possibilità di disporre di almeno due macchine Linux collegate in rete, si provi dopo aver compilato il sorgente, a lanciare il programma tenendo sul computer di destinazione un programma che "logga" i pacchetti, esempio tcpdump, si vedrà arrivare un pacchetto con un il numero IP che noi abbiamo inserito.

2.3 Single Homed Alternative Gateway: piccolo programma che usa le packet socket

Si tratta semplicemente di un programma che sfrutta il device di ethertap per generare pacchetti ethernet e spedirli attraverso la scheda reale... lo spoofing lo fa direttamente il kernel... grandioso no? Chiunque sia interessato può trovare i sorgenti qui Shag.tar.gz
MD5: 46afe328133373ebfc39263ddd52f779


Next Previous Contents