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:
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.
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.
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.
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.
Un indirizzo Internet (vedere in sezione 10 Indirizzo IP e Indirizzo 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.
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!
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.
#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.
#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:
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.
#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));
.
.
.
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.
#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.
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).
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(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.
#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).
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.
#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).
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.
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 */
};
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().
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.
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 completi si trovano su
Esempi in Windows Visual C++ (Indice)
Anche in MS Windows sono defiiti i socket in stile BSD. Vedere esempi:
nome <===> indirizzo <===> indirizzo simbolico IP ethernet
A. Rete Fisica - Ethernet (branching bus)
Rappresentazione: 2 digit esadecimali separati dal carattere :
esempio: 10:A2:F4:05 E' unico sulla rete
fisica.
Tutti gli host dello stesso bus vedono un frame trasmesso su quel bus.
B. IP Address
Puo' essere rappresentato in dotted notation.
esempio 130.251.61.103 E' unico sulla rete Internet.
La gerarchia e' raffinata da sinistra a destra, per
cui 103 indica l'host, mentre 130.251.61 indica la rete.
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.