domenica 29 maggio 2011

Cosa si nasconde dietro la notazione dotted-quad di IPv4

E' capitato che qualche studente mi chiedesse come mai gli indirizzi IPv4 hanno "quello strano formato di quattro cifre separate da punti". Ebbene la risposta e' molto semplice, e puo' essere facilmente indagata con un po' di codice.
La notazione dotted-quad presenta quattro gruppi di cifre comprese fra 0 e 255. E' facile notare che per rappresentare 256 valori (da 0 a 255) occorre esattamente un byte, e che quattro byte corrispondono a 32 bit, valore ben "noto" in informatica. E in effetti la correlazione non e' casuale: la notazione dotted-quad nasconde ciascuno dei quattro byte che corrispondono ad un numero identificativo, l'indirizzo ip appunto.
E' possibile verificare quanto detto mediante un piccolo programma C che riporto di seguito nella versione per Linux (su BSD le librerie cambiano leggermente):

#include
#include
#include
#include
#include
#include
#include

int main( int argc, char** argv ){

  if( argc < 2 ){
    perror( "\nOccorre specificare nome host e numero di porta\n");
    exit( EXIT_FAILURE );
  }


  // estraggo parametri da linea di comando
  char *hostName  = argv[1];
  char *port      = argv[2];


 

 
  // creazione nuova struttura addressinfo
  struct addrinfo *aInfo = malloc( sizeof( struct addrinfo ) * 1);

  // mi serve un puntatore (non inizializzato) per la struttura risultante
  // dal lookup di un host
  struct addrinfo *lookupResults;

  // compilo i campi della struttura
  memset( aInfo, 0, sizeof( struct addrinfo ) );      // azzeramento struttura in memoria
  aInfo->ai_family     = AF_INET;                         // utilizzo IP
  aInfo->ai_socktype   = SOCK_STREAM;                     // utilizza TCP
  aInfo->ai_protocol   = 0;                               // protocollo specificato (tutti)
  aInfo->ai_canonname  = NULL;                            // nome simbolico host
  aInfo->ai_addr       = NULL;
  aInfo->ai_next       = NULL;


  printf("\nCreata struttura addressinfo per nome simbolico host %s e servizio %s\n",
     hostName, port );


  // eseguo lookup host
  int lookupResultCode = getaddrinfo( hostName,         // nodo
                                      argv[2],         // servizio
                                      aInfo,        // informazioni per lookup
                                      &lookupResults
                      );

  // se ho codice diverso da zero allora sono in errore
  if( lookupResultCode != 0 ){
    printf( "\nRisultato lookup [%d] = %s\n", lookupResultCode,
                                          gai_strerror(lookupResultCode) );
    exit( EXIT_FAILURE );
  }

  // se sono qui ho effettuato il lookup, devo iterare su tutti i possibili
  // risultati
  struct addrinfo *currentResult;
  for( currentResult = lookupResults; currentResult != NULL; currentResult = currentResult->ai_next ){
    printf( "\n*** Risultato di lookup ***\n" );
    printf( "\n\tNome canonico:\t%s", currentResult->ai_canonname );
    printf( "\n\tLunghezza indirizzo:\t%d", currentResult->ai_addrlen );
    // conversione a struttura socket address per internet
    struct sockaddr_in *socketAddress = currentResult->ai_addr;
    // stampa dei campi
    printf( "\n\tPorta:\t%d", socketAddress->sin_port );
    struct in_addr addressIP = socketAddress->sin_addr;
    char* buffer = malloc( 24 * sizeof(char) );
    inet_ntop( AF_INET, &addressIP, buffer, currentResult->ai_addrlen );
    printf( "\n\tIndirizzo grezzo:\t%d", addressIP.s_addr );
    printf( "\n\tIndirizzo tradotto:\t%s", buffer );

    int i = 0;
    int mask = 0xFF;
    for( i = 0; i < 4; i++ ){
      printf( "\n\tPezzo di indirizzo %d:\t%d",
          i, ( (addressIP.s_addr) >> (8*i) ) & mask);
    }


    printf( "\n-----\n" );
  }

}


Il programma, seppur abbastanza lungo per essere un esempio didattico, e' molto semplice: si vuole ricerca l'indirizzo IPv4 mediante lookup (del resolver) di un sito internet e del relativo servizio, per poi estrarre i singoli pezzi dell'indirizzo e suddividerli in byte. Per meglio comprendere si consideri la seguente invocazione:

$ ./net.exe www.kde.org 80

Creata struttura addressinfo per nome simbolico host www.kde.org e servizio 80

*** Risultato di lookup ***

        Nome canonico:  (null)
        Lunghezza indirizzo:    16
        Porta:  20480
        Indirizzo grezzo:       -94370770
        Indirizzo tradotto:     46.4.96.250
        Pezzo di indirizzo 0:   46
        Pezzo di indirizzo 1:   4
        Pezzo di indirizzo 2:   96
        Pezzo di indirizzo 3:   250
-----


La parte interessante e' l'indirizzo grezzo: il valore riportato rappresenta 32 bit di informazione che identificano l'host www.kde.org. Tale indirizzo non e' facilmente memorizzabile da un umano e comunque ha una forma "strana" (e' perfino negativo!). L'indirizzo tradotto, quello in notazione dotted-quad, viene ottenuto mediante la funzione inet_ntop(..) che converte il numero unico di 32 bit in una stringa dotted-quad. Sarebbe possibile estrarre i singoli pezzi da 1 byte con una serie di "and" logici, come fa in effetti il programma a livello didattico. Tuttavia tale metodo non e' consigliato poiche' non tiene conto dell'ordinamento dei byte che dipende dalla architettura di sistema (little-endian, big-endian).
Ad ogni modo, a livello didattico, il programma preleva ogni singolo byte dell'indirizzo IPv4 e lo mette in "and" logico con 0xFF (255) al fine di estrarre ogni singolo valore della notazione dotted-quad.
Ed ecco svelato il mistero dietro alla notazione dotted-quad.

Nessun commento: