Internet: Indirizzi e Protocolli

API - Application Program Interface
Rappresentazione dei dati in Internet
Funzioni Unix per la comunicazione
Nomi e indirizzi simbolici
Libreria Unix per informazioni sul Network
Esempi
Esempio echo server e client in Windows Visual C++
Risoluzione degli indirizzi

 

API - Application Program Interface (Indice)

E' l'interfaccia fornita dal S.O. con cui l'utente puo' utilizzare i protocolli di comunicazione. In pratica sono un insieme di strutture dati e funzioni di libreria che dipendono dal S.O. e dal linguaggio utilizzato. Su Unix le API piu' usate sono:

  1. Berkeley Socket
  2. System V Transport Layer Interface (TLI)

Sono tutte sviluppate in C. Noi ci occuperemo dela Berkeley Socket. La stessa interfaccia puo' essere utilizzata per accedere a diversi protocolli e diversi spazi di indirizzi.

Berkeley Socket

Inserita inizialmente nel S.O. 4.1cBSD per VAX (1986). Supporta protocolli per Unix Domain, Internet Domain (TCP/IP e UDP/IP), Xerox Domain e vari altri. Puo' essere usata anche per la comunicazione fra processi residenti sullo stesso host.

I socket forniscono un'interfaccia sia per protocolli connection oriented (stile chiamato SOCK_STREAM) che per protocolli connectionless (stile chiamato SOCK_DGRAM, da datagram, il nome del pacchetto scambiato dal protollo IP). Un socket non e' altro che una struttura a cui corrisponde un file descriptor. D'altra parte qualsiasi cosa in Unix e' un file, quindi anche la comunicazione avviene attraverso file virtuali. Quindi per inviare o leggere un messaggio si possono usare le funzioni read e write, anche se per motivi di efficienza si preferiscono le funzioni send e receive.

Famiglie di indirizzi dei socket

Per comunicare con qualcuno dobbiamo conoscerne l'indirizzo. L'indirizzo e' di diversi tipi: indirizzo di casa, casella postale, numero di telefono ecc. Quindi possiamo definire diverse famiglie di indirizzi. Tutte le vie e le citta' sono stringhe di caratteri. Tutti i numeri di telefono sono sequenze di cifre decimali ecc.

L'analogia e' simile a quanto detto a proposito dei protocolli di comunicazione visti come collegamento telefonico o postale.

Il socket e' una struttura (definita in Linux nel file <sys/socket.h>) contenente il tipo di famiglia di indirizzi utilizzata e l'indirizzo vero e proprio. Le famiglie che considereremo sono:

L'indirizzo e' il nome di un file nel primo caso, una coppia di interi unsigned da 32 e 16 bit nel secondo caso. Entrambi i protocolli SOCK_STREAM e SOCK_DGRAM possono essere usati.

Unix Domain

Abbiamo visto che con un pipe si possono far comunicare due processi che hanno un antenato in comune che ha generato quel pipe. E' possibile far comunicare anche due processi sulla stessa macchina senza antenato comune usando la struttura socket che assume in indirizzo all'interno dello Unix Domain: si tratta di una generalizzazione dei pipe (sono ad esempio bidirezionali). Quindi il dominio degli indirizzi e' quello degli identificatori di file Unix.

La famiglia (spazio di indirizzi) si chiama AF_UNIX e consiste in path name. Ad esempio una possibile associazione e'
{ unixstr, 0, /tmp/log.01528, 0, /dev/logfile }
Si intende qui usare il protocollo SOCK_STREAM. /tmp/log.01528 e /dev/logfile sono due file (non altrimenti utilizzabili) creati per la comunicazione. La funzione bind associa un socket ad uno di questi file.

Internet Domain

Un indirizzo Internet (vedere in sezione 10  Indirizzo IPIndirizzo TCP/UDP) si memorizza in una struttura struct sockaddr_in (in Linux, nel file linux_in.h) cosi' strutturato:

............

/* Internet address. */
struct in_addr {
        __u32   s_addr;
};

............

/* Structure describing an Internet (IP) socket address. */
#define __SOCK_SIZE__   16              /* sizeof(struct sockaddr)      */
struct sockaddr_in {
  short int             sin_family;     /* Address family               */
  unsigned short int    sin_port;       /* Port number                  */
  struct in_addr        sin_addr;       /* Internet address             */

  /* Pad to size of `struct sockaddr'. */
  unsigned char         __pad[__SOCK_SIZE__ - sizeof(short int) -
                        sizeof(unsigned short int) - sizeof(struct in_addr)];
};

Ad esempio, il servizio daytime (well known port 13, sia in TCP che in UDP) sulla macchina 130.251.61.103, e' memorizzato nella variabile addr di tipo sockaddr_in come:

addr.sin_family = AF_INET;
addr.sin_port = htons(13);
addr.sin_addr.s_addr = inet_addr("130.251.61.103");

Rappresentazione dei dati in Internet (Indice)

Prima di vedere le funzioni Unix per la comunicazione, bisogna fare un'osservazione sulla rappresentazione dei valori interi all'interno della rete, che puo' unire macchine di tipo diverso.

Alcuni computer sono detti "big endian". Si fa riferimento alla rappresentazione di un intero come word, e al modo in cui e' codificato.
Una macchina e' big-endian se li memorizza in modo che il byte alto dell'intero sia il leftmost byte, mentre il byte basso e' il rightmost byte. Una Sun Sparc ad esempio e' big endian. Il numero 5 + 6 * 256 e' cosi' memorizzato:

Figura 11.1

Una macchina "little endian" li memorizza nel modo opposto: L'i386 e' little endian.

Figura 11.2

A questo punto, se una Sparc invia un intero ad un i386, quest'ultimo legge 5 + 6 * 256 come

     5 *16777216 + 6 * 65536

Se il numero indicato e' parte di un indirizzo IP, si capisce cosa puo' succedere. Per evitare tutto cio', ci si e' accordati su una rappresentazione standard di Internet.

Byte ordering e conversione degli indirizzi

Sono disponibili un insieme di funzioni per convertire i formati interni in formati Internet:

htonl - host to network, long int
htons - host to network, short int
ntohl - network to host, long int
ntohs - network to host, short int

Quali sono i valori che devono essere convertiti? Si tratta degli indirizzi che sono inclusi negli header dei pacchetti TCP, UDP e IP inviati in rete. Quindi, nella struttura struct sockaddr_in vanno convertiti i campi sin_addr e sin_port (che sono impacchettati e spediti) ma non sin_family, che e' usato dal kernel per determinare il tipo di indirizzamento usato. La conversione inversa va effettuata all'atto del ricevimento del messaggio.

Vediamo anche altre funzioni che consentono di passare dalla notazione dotted a quella 32 bit (vedere in sezione 10  dotted notation)

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

unsigned long  inet_addr(char *ptr)
char * inet_ntoa(struct in_addr in)

inet_ntoa() ha la struttura struct in_addr come argomento, non un valore long (32 bit). Inoltre ritorna un puntatore a char, cioe' ad un campo char allocato staticamente all'interno di inet_ntoa(). Ogni volta che si chiama inet_ntoa(), l'indirizzo IP chiesto per ultimo viene soprascritto.

ESEMPIO
    char *a1, *a2;
    .
    .
    a1 = inet_ntoa(ina1.sin_addr);  /* supponiamo sia 130.251.61.107 */
    a2 = inet_ntoa(ina2.sin_addr);  /* e questo e' 130.251.61.103 */
    printf("address 1: %s\n",a1);
    printf("address 2: %s\n",a2);

stampa:

    address 1: 130.251.61.103
    address 2: 130.251.61.103

Per salvare l'indirizzo occorre fare uno strcpy() su un'altra variabile.

   ina.sin_addr.s_addr = inet_addr("130.251.61.103")

In questo caso la funzione inet_addr restituisce gia' il valore in Network Byte Order. Occorre pero' fare attenzione o per un altro motivo: la funzione inet_addr restituisce -1 in caso di errore, e il valore (unsigned) -1 e' l'indirizzo IP 255.255.255.255, che corrisponde all'indirizzo di broadcast. Quindi attenzione a fare sempre i controlli!

Funzioni Unix per la comunicazione (Indice)

La definizione delle strutture dati e la loro collocazione nelle libreria puo' cambiare leggermente in dipendenza del sistema Unix in uso. Per quanto segue, ci si attiene a Linux.

socket()

   #include <sys/types.h>
   #include <sys/socket.h>

   int socket (int family, int type, int protocol)

La family e' una di quelle descritte sopra. Il type e' uno fra:

Infine il protocol e' diverso da 0 solo se si vuole utilizzare protocolli speciali invece di quelli standard di Unix.

Combinazioni di famiglie e di tipi:
 

    AF_UNIX    AF_INET 
   SOCK_STREAM 

si'

TCP

   SOCK_DGRAM

si'

UDP

   SOCK_RAW

-

IP

Noi considereremo i protocolli TCP e UDP.

Questa funzione crea un socket e ritorna un intero, cioe' un file descriptor, indicato con sockfd. Relativamente alla quintupla associata alla comunicazione, viene specificato solo il primo campo.


bind()

   #include <sys/types.h>
   #include <sys/socket.h>

   int bind (int sockfd, struct sockaddr_in *myaddr, int addrlen)

sockfd e' il fd ritornato dalla chiamata a socket. myaddr e' un pointer a struct sockaddr_in. addrlen e' la size della struttura sockaddr_in, che dipende dal dominio di indirizzi scelto, quindi basta settarlo a sizeof(struct sockaddr_in).

Si richiede di associare un socket ad un indirizzo locale. E' come mettere la casella della posta sottocasa, o istallare un telefono in casa: in questo modo si possono ricevere i messaggi inviati a quell'indirizzo. Non e' pero' sempre necessario fare una associazione fissa con un indirizzo: per inviare della posta basta infilarla nella buca delle lettere e ritirare la risposta inviata alla posta centrale, e per fare una telefonata si puo' usare un qualsiasi telefono pubblico.
Utilita' di bind:

  1. Un server ha bisogno di avere un indirizzo fisso, altrimenti i client non sanno a chi rivolgersi.
  2. Un client puo' comunque sempre registrare presso il S.O. un indirizzo fisso.

Se un processo non effettua il bind, il S.O. gli assegna comunque un indirizzo di sua scelta. Relativamente alla quintupla di comunicazione, sono identificate le due voci di local_address e local_process.

Vediamo un esempio:

ESEMPIO
    /* server */
    #include <string.h> 
    #include <sys/types.h> 
    #include <sys/socket.h> 

    #define MYPORT 3456

    /* server */
    /* Attenzione: mancano i controlli d'errore */

    main()
    {
     int sockfd;
     struct sockaddr_in my_addr;

     sockfd = socket(AF_INET, SOCK_STREAM, 0); 

     my_addr.sin_family = AF_INET;
     my_addr.sin_port = htons(MYPORT); /* short, network byte order */
     my_addr.sin_addr.s_addr = inet_addr("130.251.61.103");
     bzero(&(my_addr.sin_zero), 8);    /* azzera il resto della struttura */

     bind(sockfd, (struct sockaddr_in *)&my_addr, sizeof(struct sockaddr_in));
        .
        .
        .

Gli header files potrebbero anche differire leggermente, a seconda del tipo di Unix usato. Controllare il manuale.

Se l'utente non ha preferenze sul numero della porta logica, si puo' chiedere al sistema di assegnarne una in modo random. Si puo' anche fare a meno di assegnare l'indirizzo IP locale:

   my_addr.sin_port = 0; /* il sistema sceglie una porta random */
   my_addr.sin_addr.s_addr = htonl(INADDR_ANY);  /* my IP address */

bind(), come socket, ritorna -1 come errore, e assegna alla variabile errno il valore corrispondente al tipo di errore.


connect()

    #include <sys/types.h> 
    #include <sys/socket.h> 

    int connect(int sockfd, struct sockaddr_in *serv_addr, int addrlen);

Utilizzato per connection oriented protocol. sockfd e' il socket file descriptor ritornato da una precedente chiamata alla funzione socket(), serv_addr e' una struct sockaddr_in che contiene la descrizione (indirizzo completo) della porta remota con cui ci si vuole connettere, e addrlen la sua lunghezza in byte (sizeof(struct sockaddr_in) ).

E' una funzione bloccante: finche' la connessione non viene stabilita (o rifiutata) il processo non va avanti. Il client non ha bisogno di fare prima una chiamata a bind perche' connect assegna gli altri 4 elementi della quintupla di comunicazione, assegnando d'ufficio una porta logica, se non indicata dal client. ne' bind ne' connect specificano il protocollo (gia' definito nella struttura socket).

ESEMPIO
    #include <string.h> 
    #include <sys/types.h> 
    #include <sys/socket.h> 

    #define DEST_IP   "130.251.61.103"
    #define DEST_PORT 2345

    /* client */

    main()
    {
        int sockfd;
        struct sockaddr_in dest_addr;   /* conterra' il destination addr */

        sockfd = socket(AF_INET, SOCK_STREAM, 0); 

        dest_addr.sin_family = AF_INET;  
        dest_addr.sin_port = htons(DEST_PORT); /* short, network byte order */
        dest_addr.sin_addr.s_addr = inet_addr(DEST_IP);
        bzero(&(my_addr.sin_zero), 8);

        connect(sockfd, (struct sockaddr_in *)&dest_addr, 
                sizeof(struct sockaddr_in));
        .
        .
        .


listen

    int listen(int sockfd, int backlog);

Si tratta di una funzione chiamata dal server, o comunque dal processo che riceva una richiesta di connessione. Si puo' pensare ad un centralino che mette in attesa le chiamete finche' non si libera il telefono. backlog e' il numero delle richieste di connessioni che possono essere memorizzate nella coda d'attesa. Quando il server e' libero, esegue una accept. Un numero usato e' 5 o 10.

Prima di chiamare questa funzione e' necessario aver fatto una chiamata a bind, in modo che il kernel sappia da che porta deve ricevere i messaggi.


accept

     #include <sys/socket.h> 

     int accept(int sockfd, struct sockaddr_in *peer, int *addrlen);

peer e' un puntatore alla struttura locale struct sockaddr_in. addrlen e' il solito sizeof(struct sockaddr_in).

Accetta la prima richiesta di connessione nella coda d'attesa, altrimenti blocca l'esecuizione del processo finche' non ne arriva una. Accettando la connessione, assegna alla struttira peer l'indirizzo completo del client richiedente (e' contenuto nel messaggio di richiesta).

Questa funzione, oltre ad accettare una connessione, genera un altro socket di cui viene passato il socket file descriptor come parametro di ritorno. La connessione insomma (la quintupla) si stabilisce non fra il socket originale del server e quello del client, ma fra il nuovo socket del server e il client.

Vediamo di andare avanti con l'esempio precedente:

ESEMPIO
    #include <string.h> 
    #include <sys/types.h> 
    #include <sys/socket.h> 

    #define MYPORT 2345    /* port number del server */
    #define BACKLOG 5     /* chiamate accodate al massimo */

    /* server */

    main()
    {
        int sockfd, new_fd;  
        struct sockaddr_in my_addr;
        struct sockaddr_in their_addr; /* address information del chiamante */
        int sin_size;

        if (( sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0);
                { perror(""socket  error");  exit(); }

        my_addr.sin_family = AF_INET;         
        my_addr.sin_port = htons(MYPORT);     
        my_addr.sin_addr.s_addr = htonl(INADDR_ANY); 
        bzero(&(my_addr.sin_zero), 8);     

        if (bind(sockfd, (struct sockaddr_in *)&my_addr, 
                            sizeof(struct sockaddr_in)) < 0)
                { perror(""bind error");  exit(); }

        if (listen(sockfd, BACKLOG) < 0)
                { perror(""listen error");  exit(); }

        sin_size = sizeof(struct sockaddr_in);
        if (( new_fd = accept(sockfd, &their_addr, &sin_size)) < 0)
                { perror(""accept error");  exit(); }
        .
        .
        .

Quindi, per effettuare delle send e receive il server deve usare il nuovo socket file descriptor. Volendo, si puo' chiudere con close quello originale (non ci si potra' piu' connettere con quel socket). Continuiamo con l'esempio precedente, ma al posto dell'ultima riga, con la chiamata ad accept, inseriamo quanto segue:

ESEMPIO
      .
      .
    for (;;) 
    {
        if (( new_fd = accept(sockfd, &their_addr, &sin_size)) < 0)
                { perror(""accept error");  exit(); }
        if (fork()==0) {close(sockfd);   /* process figlio */
                        doit(new_fd);    /* processa la richiesta */
                        exit(1); }
        close (new_fd);
    }
      .
      .

accept trova 3 elementi della quintupla della comunicazione (i primi 3) in <sockfd> e li passa a new_fd. In piu' quest'ultimo si lega ai foreign-addr e process-addr ottenuti da accept. Quindi il parent chiude new_fd, ma puo' ancora utilizzare sockfd perche' rimane incompleto, e quindi completabile con una nuova accept.


send, recv

Funzioni di comunicazione connection oriented. Non richiedono l'indirizzo del destinatario come parametro in quanto utilizzano la connessione gia' stabilita.

    int send(int sockfd, const void *msg, int len, int flags);

msg e' un puntatore al buffer che va spedito, len la lunghezza in bytes. flags va a 0.

ESEMPIO
    char *msg = "MESSAGGIO"
    int len, bytes_sent;
    .
    .
    len = strlen(msg);
    bytes_sent = send(sockfd, msg, len, 0);
    .
    .

send ritorna il numero di byte effettivamente inviati.

   int recv(int sockfd, void *buf, int len, unsigned int flags);

buf e' il buffer in cui si scrive quanto ricevuto. len e' la lunghezza del buffer (il massimo numero di byte che si vuole leggere), flags vale 0. Ritorna il numero di byte effettivamente letti, oppure -1. Se il mittente invia 100 byte, potrebbero essere letti anche meno di 100 byte. Per essere sicuri di averli letti tutti, occorre ripetere anche piu' di una volta l'operazione recv (vedere esempio echo_client.c).


sendto, recvfrom

Sono operazioni su datagram, che non usano la connessione. Occorre ogni volta indicare anche l'indirizzo del mittente. Per il resto si comportano come send e recv.

    int sendto(int sockfd, const void *msg, int len, unsigned int flags,
               const struct sockaddr_in *to, int tolen);

    int recvfrom(int sockfd, void *buf, int len, unsigned int flags
                 struct sockaddr_in *from, int *fromlen);


close, shutdown

    close(sockfd);

    int shutdown(int sockfd, int how);

close chiude la connessione, per cui chiunque stia ancora cercando di leggere o scrivere su quel socket riceve un messaggio d'errore, e se ne accorge. shutdown consente la chiusura anche in una sola delle due direzioni.
 
 

Funzioni Unix che consentono di avere 
l'indirizzo IP o il nome simbolico di un host o di un server
(Indice)

getpeername

    #include <sys/socket.h> 
    int getpeername(int sockfd, struct sockaddr_in *addr, int *addrlen);

Dice chi c'e' dall'altra parte di una connessione. sockfd e' il descrittore del socket connesso, addr e' un puntatore a un valore di tipo struct sockaddr_in che conterra' l'informazione desiderata addrlen va inizializzato al solito sizeof(struct sockaddr_in).


hostid

    int hostid()

Ritorna l'indirizzo IP (32 bit) del computer locale. Si tratta di una valore che deve essere preimpostato dal supervisor tramite la funzione privilegiata sethostid(hostid), in network byte order.


gethostname

    #include <unistd.h>
    int gethostname(char *hostname, size_t size);

hostname punta ad un array di caratteri di lunghezza size contenente il nome del computer locale. Si tratta di una valore che deve essere preimpostato dal supervisor tramite la funzione privilegiata sethostname(name, len).
 
 

Libreria Unix per informazioni sul Network (Indice)

Non si tratta di funzioni del S.O. Unix, ma di un insieme di procedure che possono essere eseguite dallo user. Si puo' accedere ad un data base di informazioni (gestite dall'amministratore di rete).

Assegnazione nome-indirizzo IP
E' necessario un meccanismo di conversione per trasformare i nomi simbolici di degli host in indirizzi IP e viceversa.

  1. Schema statico: possono essere mantenuti dei file (/etc/hosts, /etc/services) oppurre veri data base relazionali o a offetti, tipo NIS+ (detto anche Yellow Pages), NDS ecc.
  2. schema dinamico: ad esempio NBP di Apple Talk (costruzione dinamica di tabelle). E' necessaria una interrogazione di rete per sapere se un servizio esiste.

I risultati di una interrogazione possono passare il risultato in parametri di tipo hostent, netent o servent. Questi tipi sono definiti nel file include netdb.h

Parte di netdb.h
/*
 * Structures returned by network data base library.  All addresses are
 * supplied in host order, and returned in network order (suitable for
 * use in system calls).
 */

struct  hostent {
        __const
        char    *h_name;        /* official name of host */
        char    **h_aliases;    /* alias list */
        int     h_addrtype;     /* host address type */
        int     h_length;       /* length of address */
        char    **h_addr_list;  /* list of addresses from name server */
#define h_addr  h_addr_list[0]  /* address, for backward compatiblity */
};


struct  netent {
        char            *n_name;        /* official name of net */
        char            **n_aliases;    /* alias list */
        int             n_addrtype;     /* net address type */
        unsigned long   n_net;          /* network # */
};

struct  servent {
        char    *s_name;        /* official service name */
        char    **s_aliases;    /* alias list */
        int     s_port;         /* port # */
        char    *s_proto;       /* protocol to use */
};

struct  protoent {
        char    *p_name;        /* official protocol name */
        char    **p_aliases;    /* alias list */
        int     p_proto;        /* protocol # */
};

struct rpcent {
        char    *r_name;        /* name of server for this rpc program */
        char    **r_aliases;    /* alias list */
        int     r_number;       /* rpc program number */
};

ptr = gethostbyname (namestr)
ptr = gethostbyaddr (addr, len, type)

gethostbyname restituisce l'indirizzo IP (32 bit) dell'host di cui si conosce il nome (namestr).

    #include <netdb.h> 
    struct hostent *gethostbyname(const char *name);

Ritorna un puntatore ad una struct hostent.

ESEMPIO
    #include <stdio.h> 
    #include <stdlib.h> 
    #include <errno.h> 
    #include <netdb.h> 
    #include <sys/types.h>
    #include <netinet/in.h> 

    int main(int argc, char *argv[])
    {
        struct hostent *h;

        if (argc != 2) {  /* error check the command line */
            fprintf(stderr,"usage: getip address\n");
            exit(1);
        }

        if ((h=gethostbyname(argv[1])) == NULL) {  /* get the host info */
            herror("gethostbyname");
            exit(1);
        }

        printf("Host name  : %s\n", h->h_name);
        printf("IP Address : %s\n",inet_ntoa(*((struct in_addr *)h->h_addr)));

        return 0;
    }

gethostbyname() non usa lo standard errno, ma il proprio h_errno, stampato da herror() invece che da perror().


ptr = getnetbyname (namestr)
ptr = getnetbyaddr (netaddr, addrtype)

La prima funzione ritorna un puntatore ad un long contenente l'indirizzo IP della rete di cui si e' passato il nome simbolico. La seconda ritorna un puntatore alla struttura netent.


ptr = getservbyname (namestr, proto)
ptr = getservbyaddr (port, proto)

La seconda funzione restituisce il puntatore ad una struttura servent. Occorre specificare anche il protocollo, oltre al numero della porta (16 bit).


E' nche possibile conoscere e modificare il comportamento di un socket, usando le funzioni

getsockopt (socket, level, optionid, optionval, length)
setsockopt (socket, level, optionid, optionval, length)

Ad esempio, e' possibile inserire dei time out sulle operazioni di trasmissione e ristrsmissione, alloacare buffer di diversa size, effettuare trasmissioni "urgenti" ecc.


nready = select ( ndesc, indesc, outdesc, excdesc, timeout)

Consente di effettuare dei "probe": controlla se ci sono file descriptor pronti per l'I/O, riferendosi alla tavola dei FD:

Serve ad esempio per aspettare richieste da piu' porte, attivandosi non appena ad una di esse arriva un messaggio. I FD ready sono indicati dai bit rimasti ad 1 nelle maschere parametri.
 
 

Esempi (Indice)

Esempi completi si trovano su

Esempi in Windows Visual C++ (Indice)

Anche in MS Windows sono defiiti i socket in stile BSD. Vedere esempi:

echo server , echo client .

Risoluzione degli indirizzi (Indice)

nome        <===>   indirizzo     <===>   indirizzo
simbolico           IP                    ethernet

A. Rete Fisica - Ethernet    (branching bus)

Tutti gli host dello stesso bus vedono un frame trasmesso su quel bus.

B. IP Address

Due reti fisiche separate possono comunicare tranite un gateway.
Normalmente una sottorete e' formata da una o piu' reti fisiche.

    net1     -----          net2
  -----------| G |-----------------
  |          -----      |         |   130.251.61.103
  |                     |         |
  ----                  -----------
 130.251.121.227


Il gateway G ha un doppio indirizzo, uno per la rete 130.251.61 (ad esempio 130.251.61.12) e un altro per l'altra rete (ad esempio 130.251.121.43).

C. Nome Simbolico (DNS)

Si usa il Domain Name System (DNS) (1984). Gerarchia di domini organizzati per raffinamenti gerarchici da sinistra a destra.
esempio disi.unige.it.
Nota: gli host non sono necessariamente in Italia, e nemmeno al DISI.
Il nome e' unico su tutta la rete Internet.

Host name resolution       Nome simbolico =======> IP address

Per trovare l'indirizzo IP conoscendo il nome simbolico, si controlla nella Tavola locale. Se non c'e' si fa una query al name server della rete locale. Se anche questo non ha l'informazione, ci si rivolge ad un name server gerarchicamente superiore. Alla fine si trova l'instradamento, cioe' la sequenza di host da cui bisogna passare per fare arrivare il messaggio.

Le informazioni sul routing (strada da percorrere) vengono mantenute in memoria per un certo tempo TTL (time-to-live) nella cache locali per successivi eventuali messaggi. Sono possibili anche richerche inverse.

Address resolution         IP address ========> Ethernet address

Individuata la rete fisica (o e' quella locale, o e' il gateway da cui si deve passare) si guarda in una memoria cache locale se la corrispondenza IP address - indirizzo Ethernet e' gia' nota.
Altrimenti si usa ARP (Address Resolution Protocol).

Viene fatto un broadcast sulla rete fisica con l'indirizzo IP cercato. L'host owner risponde con il suo indirizzo ethernet. La cache e' mantenuta anch'essa per un tempo limitato. E' possibile anche la ricerca inversa.

ESEMPIO

Supponiamo che Elios, Cartesio e Alfa siano dei gateway. Pippo invia un messaggio a Perceval.
Con un'interrogazione al DNS scopre che l'indirizzo IP e' 130.251.60.4, quindi non si trova nella stessa sottorete. Pippo chiede al server locale (Elios) come fare a raggiungere la rete 130.251.60.0:

Tabella di Elios (contiene riferimenti alle due reti di cui e' gateway):
 

Rete Gateway Interfaccia
130.251.21.0                - fddi0
130.251.24.0               130.251.23.2                  fddi0
130.251.61.0               - eth0
         .......                 ........         .......
0.0.0.0                         130.251.23.2                    fddi0

L'ultima riga indica il default route, ossia se quanto si cerca non e' presente nella tabella, si rivolge un'interrograzione al gateway indicato.

Non avendo indicazioni per la rete 130.251.60, si fa la richiesta ad Alfa. Alfa possiede l'indicazione di Cartesio come gateway per la rete cercatae invia il suo indirizzo a Elios. I pacchetti successivi sono inviati direttamente a Cartesio (si cerca un routing ottimale).
inserimento di Cartesio nella Tabella di Elios (eventualmente).

Si puo' anche configurare una rete locale non connessa in Internet. in questo caso gli indirizzi IP possono essere decisi dal sistemista.

ESEMPIO

Situazione della ARP Table sulla macchina arsenio.disi.unige.it:

krypton > arp -a
elios.disi.unige.it (130.251.61.19) at 08-00-20-74-b5-e1 stale
spawn.disi.unige.it (130.251.61.75) at 00-00-b4-68-99-0e
disisco (130.251.61.254) at 00-00-0c-18-f5-6e
arsenio.disi.unige.it (130.251.61.103) at 00-20-18-28-c5-12 stale
biblio4.sv.inge.unige.it (130.251.121.227) at 00-00-0c-18-f5-6e stale

Provo a collegarmi con un ping al gateway dello CSITA. L'informazione di come raggiungere lo CSITA non e' presente nella tavola locale di arsenio.

krypton > ping csita.unige.it
PING csita.unige.it (130.251.21.18): 56 data bytes
64 bytes from 130.251.21.18: icmp_seq=0 ttl=253 time=28 ms
64 bytes from 130.251.21.18: icmp_seq=1 ttl=253 time=17 ms
64 bytes from 130.251.21.18: icmp_seq=2 ttl=253 time=31 ms
64 bytes from 130.251.21.18: icmp_seq=3 ttl=253 time=49 ms

^C
----csita.unige.it PING Statistics----
4 packets transmitted, 4 packets received, 0% packet loss
round-trip (ms)  min/avg/max = 17/31/49 ms

Per raggiungere il gateway CSITA arsenio ha dovuto contattare il server e farsi indicare il routing. L'indirizzo del gateway CSITA e' provvisoriamente inserito nella ARP Table:

krypton > arp -a
elios.disi.unige.it (130.251.61.19) at 08-00-20-74-b5-e1
igecuniv.csita.unige.it (130.251.21.18) at 00-00-0c-18-f5-6e
spawn.disi.unige.it (130.251.61.75) at 00-00-b4-68-99-0e
disisco (130.251.61.254) at 00-00-0c-18-f5-6e stale
arsenio.disi.unige.it (130.251.61.103) at 00-20-18-28-c5-12
biblio4.sv.inge.unige.it (130.251.121.227) at 00-00-0c-18-f5-6e stale

ESEMPIO

******** Definizione delle tavole di routing *******
arsenio > route
Kernel routing table
Destination     Gateway         Genmask         Flags MSS    Window Use Iface
130.251.61.0    *               255.255.255.0   U     1500   0       74 eth0
loopback        *               255.0.0.0       U     3584   0        2 lo
default         disisco         *               UG    1500   0       60 eth0

ESEMPIO

netstat e' un comando che visualizza lo stato delle connessioni del network (TCP, UDP, RAW, Unix socket)

******** Tavole dei servizi (porte connesse e/o attive)  *******

arsenio > netstat -a
Active Internet connections (including servers)
Proto Recv-Q Send-Q Local Address          Foreign Address        (State)       User
tcp        0      0 *:sunrpc               *:*                    LISTEN        root       
tcp        0      0 *:echo                 *:*                    LISTEN        root       
tcp        0      0 *:discard              *:*                    LISTEN        root       
tcp        0      0 *:daytime              *:*                    LISTEN        root       
tcp        0      0 *:chargen              *:*                    LISTEN        root       
tcp        0      0 *:time                 *:*                    LISTEN        root       
tcp        0      0 *:ftp                  *:*                    LISTEN        root       
tcp        0      0 *:telnet               *:*                    LISTEN        root       
.............
tcp        0      0 arsenio:telnet         urano:34277            ESTABLISHED   root       
tcp        0      2 arsenio:login          krypton:1023           ESTABLISHED   root       
udp        0      0 *:syslog               *:*                    
udp        0      0 *:sunrpc               *:*                    
udp        0      0 *:echo                 *:*                    
.............
raw        0      0 *:1                    *:*                    
Active UNIX domain sockets
Proto RefCnt Flags      Type            State           Path
unix  2      [ ]        SOCK_STREAM     UNCONNECTED     49370

E' la lista dei socket attivi che non sono in stato di LISTEN, cioe' sono connessi a processi attivi: nel momento del comando non c'erano.