Capitolo 4 Realizzazione del proxy

Abbiamo esposto il problema, dove si deve intervenire, e quali strumenti si hanno a disposizione: al lavoro, dunque.

4.1 Ingannare gli endpoint

Come abbiamo detto in un capitolo precedente, la presenza di un ALG deve essere trasparente agli utenti. Per rispettare questo requisito dobbiamo "ingannare" gli endpoint: ogni terminale deve "convincersi" di comunicare con un terminale attestato sulla sua stessa rete.

L'ambiente di test è quello illustrato in figura 4.1:

Figura 4.1 Ambiente di test

Supponiamo già effettuate le fasi iniziali della chiamata; lo scambio dei messaggi H.245 segue lo schema illustrato in figura 4.2

Figura 4.2 Fallimento apertura dei canali logici

 

 

Nell' OLC mandato da Alice a Bob c'è l'indirizzo RTCP di Alice, ad esempio:

RTCP Alice= 10.244.116.107 : 12001

La prima parte è l'indirizzo IP dell' host Alice, mentre 12001 è un numero di porta UDP, sulla quale Alice dichiara di essere pronta a ricevere messaggi del protocollo RTCP.

L'MC inoltra il messaggio a Bob, che immagazzina l'informazione, e restituisce un messaggio OLC ACK che contiene il proprio indirizzo RTCP e quello RTP, ad esempio:

RTCP Bob= 192.168.0.2: 5001

RTP Bob = 192.168.0.2: 5000

Al solito, la prima parte è l'indirizzo IP di Bob, mentre 5000 e 5001 sono le porte adiacenti UDP sulle quali Bob si dichiara pronto a ricevere, rispettivamente, controllo RTCP e dati multimediali codificati RTP.

L'MC inoltra il messaggio ad Alice, che ora è pronta ad aprire un canale logico unidirezionale verso la porta UDP 5000 di Bob;

I pacchetti contenenti i dati multimediali vengono confezionati, ma conterranno nel campo destination dell'intestazione IP un indirizzo IP non appartenente alla rete visibile da Alice. Essi verranno allora spediti verso un gateway di default eventualmente presente sulla rete 10.244.116.x, e da questo scartati.

Lo scambio OLC-OLC ACK nella direzione opposta segue esattamente la stessa sorte, e nessuno dei due endpoint riesce a "sentire" l'altro.

L'inganno

Ogni volta che all' MC arriva un messaggio, esso ne estrae informazioni, che va a memorizzare in apposite strutture dati. Attraverso le API fornite possiamo variare il valore di alcuni campi di queste strutture prima che il Media Controller, a partire da queste, confezioni il messaggio uscente.

La decisione sull'intervenire o meno è data dallo stato di un flag, settato dal gatekeeper in base al contenuto del messaggio ARQ, dal quale si può desumere l'indirizzo IP di chiamante e chiamato. Ovviamente, se il chiamante e il chiamato appartengono alla stessa rete, non c'e bisogno di darsi tanta pena.

Supponiamo d'ora in poi di essere nella situazione in cui il chiamante e il chiamato non si trovano sulla stessa rete.

Riferendoci alla figura 4.3, che illustra il diagramma di stato relativo all'apertura di un canale:

    1. All' MC arriva un OLC da Alice: all'interno c'è l'indirizzo RTCP di chi l' ha spedito, vale a dire IP+porta UDP, che possiamo andare a "leggere" servendoci dell'API apposita
    2. Apertura canale

      Figura 4.3Intervento sui messaggi OLC e OLC ACK

    3. Viene inserito un controllo nel codice originale:

    1. Viene restituito il flusso al codice originale, che confeziona un messaggio OLC da inoltrare a partire dal nuovo indirizzo RTCP
    2. L' endpoint remoto (Bob) riceve l'OLC modificato: all'interno trova l'informazione [indirizzo RTCP= Indirizzo IP + porta UDP]. Il primo campo è ora un indirizzo IP appartenente alla sua stessa rete, e più precisamente l'indirizzo dell'interfaccia eth1 di Urania : 192.168.0.1. Sarà a questo indirizzo che sarà diretto il messaggio di conferma OLC ACK.
    3. L'endpoint remoto spedisce il messaggio OLC ACK all' MC
    4. L' MC riceve il messaggio OLC ACK. Possiamo "leggere" in tale messaggio, sempre per mezzo delle API suddette, le informazioni che ci interessa modificare: gli indirizzi RTCP e RTP del mittente, cioè Bob.
    5. Altro controllo inserito nel codice. Per ciascuno dei due indirizzi:

    1. Viene restituito il flusso al codice originale, che confeziona un messaggio OLC ACK da inoltrare a partire dai nuovi indirizzi
    2. L'endpoint Alice riceve l'OLC ACK modificato, nel quale trova le informazioni

Il primo campo è ora un indirizzo della rete di Alice, e precisamente l'indirizzo dell'interfaccia eth0 di Urania. Sarà a quest'indirizzo che Alice spedirà i pacchetti RTP contenenti i dati multimediali.

Ripetendo il procedimento per il canale aperto da Bob verso Alice, siamo giunti alla situazione in figura 4.4.

La combinazione GK+MC fa routing dei flussi H.225 e H.245 , mentre agli endpoint è stato "detto" di spedire i pacchetti RTCP e RTP a indirizzi che possono raggiungere, e cioè quelli delle interfacce di rete di Urania: qui i pacchetti si fermano, dato che non c'è un'applicazione H.323 che sia pronta a riceverli. La chiamata continua a non funzionare.

 

Figura 4.4 I pacchetti RTP si fermano sulle interfacce Ethernet della

macchina a cavallo tra le due reti

 

4.2 Far arrivare i pacchetti a destinazione

I pacchetti si fermano sulle interfacce di rete eth0 e eth1 perché nel loro header UDP è quello l'indirizzo di destinazione specificato. Se vogliamo che vadano da qualche altra parte dobbiamo intervenire su quel campo: dobbiamo fare Destination NAT.

Per fare sì che le due interfacce di rete sull'host Urania si possano "vedere", vale a dire che i pacchetti possano passare dall'una all'altra, dobbiamo abilitare su tale host una funzione detta IP forwarding. Questo viene fatto mediante il comando (inserito nel codice C originale)

$ echo 1 > /proc/sys/net/ipv4/ip_forward

Una volta utilizzato questo accorgimento siamo pronti a modificare i pacchetti a mano a mano che arrivano sulle interfacce.

 

La tabella di NAT di Netfilter

Riprendiamo lo schema esposto nel capitolo 3 che descriveva l'attraversamento dei pacchetti.

Figura 4.5 Attraversamento dei pacchetti

Se consideriamo anche le "catene" della tabella di NAT, inizialmente vuota, modifichiamo lo schema nel modo seguente:

Figura 4.6 Inserimento delle catene Per e Post Routing della tabella NAT

Esiste la possibilità di intervenire sui pacchetti prima ancora che su di essi venga fatta la decisione di routing, quando attraversano la "catena" detta appunto di PREROUTING, e appena prima che vadano in uscita, nella catena detta POSTROUTING.

Quando abbiamo "ingannato" gli endpoint, li abbiamo costretti a cambiare l'indirizzo cui spedire il flusso RTP/RTCP da quello irraggiungibile dell'endpoint remoto col quale la comunicazione deve essere instaurata a quello raggiungibile di una macchina attestata sulla stessa rete. Per ogni connessione teniamo traccia dell'indirizzo "soppresso", e lo restituiamo ai pacchetti con un'operazione di Destination NAT. Chiariamo il procedimento con un esempio. Il generico pacchetto UDP proveniente da Alice avrà nella propria intestazione:

 

 

Figura 4.7 Pacchetto UDP

Come già detto, nella fase in cui il codice C aggiunto a quello originale operava le sostituzioni di indirizzo, abbiamo immagazzinato indirizzo IP e porte UDP del reale destinatario dei pacchetti in strutture dati appositamente create. Nella catena di PREROUTING aggiungiamo delle regole costruite a partire da tali informazioni, che restituiranno ai pacchetti la reale destinazione; i pacchetti così modificati verranno indirizzati alla catena FORWARD, attraverso la quale usciranno sull'interfaccia di rete "giusta", raggiungendo finalmente il destinatario.

Figura 4.8 Destination NAT per i pacchetti da Alice verso Bob

In figura 4.8 è illustrato il risultato della sostituzione per i soli pacchetti che viaggiano da Alice verso Bob; nell'altra direzione la situazione è analoga.

In realtà il processing dei pacchetti non si ferma qui: nella catena di POSTROUTING applicheremo una regola di Source NAT, in modo che il pacchetto uscente sembri provenire da una delle due interfacce Ethernet di Urania. La doppia sostituzione è illustrata in figura 4.9, sempre per una sola direzione.

 

Figura 4.9 DNAT e SNAT

 

Riassumendo la situazione:

La chiamata (finalmente) funziona: i due utenti si sentono, e, dato che la procedura descritta viene ripetuta per ogni canale logico, si possono anche vedere, se dispongono del necessario hardware.

Figura 4.10 Chiamata a buon fine

 

 

 

 

 

 

 

 

 

 

 

 

 

 

    1. Modifiche in fase di chiusura del canale: rimozione delle regole
    2. Fino ad ora ci siamo occupati della sola parte relativa all'apertura dei canali logici, intercettando i messaggi H.245, deviando i pacchetti, facendo NAT.

      Nella fase di chiusura interverremo in maniera analoga, dato che la chiusura di un canale logico è regolata da uno scambio di messaggi molto simile.

      Figura 4.11 Scambio di messaggi per la chiusura di un canale

      In questa fase dobbiamo fare meno cose; infatti il messaggio Close Logical Channel indica che il mittente vuole chiudere un canale logico, e basta inoltrarlo così com'è, poiche il destinatario non trae dal messaggio informazioni che ne influenzano il comportamento. La stessa cosa vale per il messaggio di conferma.

      La cosa che non dobbiamo dimenticare è di cancellare le regole di NAT aggiunte nelle catene di PRE e POST-ROUTING per il canale logico in questione: come detto in precedenza, si è stabilito di tenere traccia di ogni chiamata mediante strutture logiche create appositamente. Alla fine di una chiamata siamo in grado di risalire a tutti i canali logici aperti per essa, e di cancellare tutte le regole relative nelle catene del Netfilter.

       

      Figura 4.11 Chiusura di un canale logico

       

    3. Estensione al caso di più chiamate contemporanee: strutture dati create
    4. Le modifiche fatte al codice permettono a questo punto di portare a buon fine una chiamata a cavallo tra una rete pubblica e una privata: dato che il Media Controller originale supporta fino a 100 chiamate contemporanee, estendiamo la possibilità di più chiamate contemporanee anche al proxy realizzato.

      Il codice originale offre la possibilità di ottenere (mediante l'uso delle solite API) un'identificativo numerico per ogni conferenza, uno per ogni "leg" di una conferenza (gamba, come comunemente detto in riferimento a una parte di una chiamata multi-parte), e uno per ogni canale logico.

      In figura 4.12 è ilustrata una chiamata vista secondo quest'ottica.

       

      Figura 4.12 Terminologia usata dall' MC per descrivere una conferenza

      Per immagazzinare queste ed altre informazioni relative alle conferenze in corso, e per tenere traccia delle modifiche fatte alla tabella di routing della macchina che ospita il proxy, in modo da poterle eliminare una volta finite le conferenze, abbiamo usato delle strutture dati del linguaggio C "personalizzate" per i nostri scopi. Inoltre sono state scritte varie funzioni C, che sono servite per effettuare i controlli sull'appartenenza degli host all'una o all'altra sottorete, per la conversione dei formati degli indirizzi, e per permettere al codice di conoscere l'indirizzo IP associato alle interfacce Ethernet della macchina sulla quale gira.

       

      1. Strutture dati

"Indirizzo"

struct indirizzo { char ip[100];

int port;

bool scritto;

}

La struttura indirizzo serve, come appare chiaro, a immagazzinare uno TSAPIdentifier, costituito da un indirizzo IP e da un numero di porta. Il flag "scritto" è una variabile booleana che ci ricorda se nel corso della chiamata abbiamo scritto qualcosa di significativo nella struttura: senza il controllo esercitato da tale flag il codice potrebbe pescare dai due campi precedenti dei numeri a caso.

"Leg"

struct leg { mciCallHandleType numchiamata;

bool attiva;

}

Una call leg è una delle due parti di una conferenza, considerata come l'unione di una parte che va dal primo endpoint al proxy, e da un'altra parte che va dal proxy al secondo endpoint. Il primo campo della struttura non è altro che un identificativo numerico, espresso però secondo un tipo definito dal fornitore dell'MC.

Il flag "attiva" ha la stessa funzione che aveva "scritto" nel caso precedente, e interviene anche nella chiusura di una conferenza.

Quando entrambe le "leg" di una conferenza sono disattivate (vale a dire quando tutti i canali sono stati chiusi), allora la chiamata può considerarsi chiusa: è allora e solo allora che andiamo a cancellare le regole di NAT dalle catene del Netfilter.

 

 

 

 

 

 

 

 

"Conferenza"

struct conferenza {

mciConfHandleType id;

leg leg1;

leg leg2;

indirizzo RTCP_IN;

indirizzo RTCP_OUT;

indirizzo AUDIO_IN;

indirizzo AUDIO_OUT;

indirizzo DATA_IN;

indirizzo DATA_OUT;

indirizzo VIDEO_IN;

indirizzo VIDEO_OUT;

bool attiva;

bool inoltratoRTCP;

bool inoltratoAUDIO;

bool inoltratoVIDEO;

bool inoltratoDATA;

}

Questa struct contiene tutti i campi di interesse relativi a una singola conferenza:

    1. distinguiamo la sottorete dalla quale arriva il messaggio; supponiamo che sia la rete che consideriamo "esterna"
    2. Immagazziniamo l'informazione ricavata (l'indirizzo RTCP del mittente) nel campo RTCP_OUT
    3. Riceviamo dopo un certo intervallo di tempo il messaggio OLC ACK: in questo troviamo l'indirizzo RTCP dell'altro endpoint.
    4. E' solo ora che possiamo aggiungere una regola di NAT, che suonerà come: "Tutti i pacchetti UDP che arrivano sulla tale interfaccia di rete, dal tale indirizzo sorgente e dalla tale porta, e che sono destinati a un certo indirizzo e a una certa porta, devono essere prima Nattati e poi spediti su quest'altra interfaccia di rete"

Dato che viene aperto un canale logico per ogni tipo di media possibile in una chiamata H.323, sono stati previsti tutti i casi. Nei messaggi H.245 relativi ai canali logici è specificato anche il tipo di media che deve viaggiare sul canale: questa informazione viene letta dai messaggi e gli indirizzi immagazzinati nei campi corretti

4.4.2 Funzioni C

Riportiamo solo l'intestazione delle funzioni scritte e il loro impiego, rimandando all'appendice A per il codice completo.

Getaddress

unsigned char *getaddress( char *interfaccia)

Rileva l'indirizzo ip assegnato all'interfaccia che prende come argomento e restituisce un puntatore a un unsigned char array

Duealla

long int duealla ( int esponente)

Funzione che riceve in ingresso l'esponente e dà in uscita la potenza di base due di quell'esponente come long int

Bintodec

int bintodec( int array[8])

Prende un numero binario espresso come stringa di 8 caratteri e restituisce un intero

Bintodec32

long int bintodec32( char array[32])

Prende un numero binario espresso come stringa di 32 caratteri e restituisce un intero

Numtostring

char *numtostring( long int n)

Converte gli indirizzi dati nel formato long int in indirizzi espressi in dotted notation, in una stringa. Richiede "duealla"

Dottobininv

char *dottobininv (char *dot)

Converte un indirizzo dotted in una stringa di bit, invertendo i quattro ottetti risultanti. Richiede  "duealla"

Dottobin

char *dottobin (char *dot)

Converte un indirizzo dotted in una stringa di bit. Richiede  "duealla"

Inttobin32

char *inttobin32 ( long int numero)

Converte un indirizzo in formato long int in un array di 32 bit. Richiede "numtostring" e "dottobininv"

Strrev

char *strrev (char *string)

Inverte i caratteri di una stringa. E' una funzione di libreria, non l'ho scritta io.

Same

bool same( char *indirizzo1, long int indirizzo2, int netmask)

Dice se due indirizzi appartengono alla stessa sottorete: il primo è una stringa, il secondo un long int. Il terzo è la maschera di sottorete espressa come numero di bit. Richiede "dottobininv", "inttobin32", "strrev"

 

 

 

 

 

 

 

 

 

 

4.5 Funziona! Esempi in vari scenari di test

Il software realizzato è stato testato in varie situazioni, nelle quali sono stati usati, di volta in volta, endpoint H.323 di diversi fornitori, e operanti su sistemi operativi differenti. Diamo un elenco degli strumenti usati:

 

Endpoint

Sistema operativo

Tipo di endpoint

Stack H.323 usato

Ohphone

Linux

Applicazione software

Open-source

Openphone

Windows

Applicazione software

Open-source

Netmeeting

Windows

Applicazione software

Proprietario

Innovaphone Tiptel

Proprietario

Telefono IP

Proprietario

GnomeMeeting

Linux

Applicazione software

Open-source

Il gatekeeper e il Media Controller a partire dai quali è stato realizzato il software sono prodotti da Radvision, un vendor che ha implementato uno stack H.323 proprietario, e le versioni di Linux sulle quali "gira" il software sono quelle che montano un Kernel 2.4.7-10 (quale può essere trovato nella distribuzione Red Hat 7.1.

I risultati ottenuti sono confortanti, ma anticipiamo subito che in alcuni casi le chiamate non sono andate a buon fine. Sono stati allora ripetuti i test attestando i due endpoint sulla stessa rete e provando a effettuare delle chiamate con o senza gatekeeper (senza usare l'MC). Quello che si è trovato è che alcuni endpoint avevano dei problemi di interoperabilità anche nel caso di chiamate dirette, dovuti a implementazioni dello stack H.323 non compatibili (vedi Capitolo 1, sezione pro e contro), mentre in un numero piccolo, ma pur sempre presente, di casi, i problemi sono da ricercarsi nel codice scritto.

Per ciascun test sono state effettuate 4 prove: ogni terminale è stato attestato, di volta in volta, sulla rete privata o su quella pubblica, ed ha chiamato o è stato chiamato dall'altro terminale.

 

 

 

 

 

 

Test 1: Ohphone (Linux)ß -à Tiptel (Innovaphone)

 

 

Chiamante

Chiamato

Esito

Ohphone

Tiptel

Il chiamante non riceve l'audio

Tiptel

Ohphone

Tutto OK!

Test 2 : Ohphone (Linux) ß -à Netmeeting (Windows)

 

Chiamante

Chiamato

Esito

Ohphone

Netmeeting

Chiamata non a buon fine

Netmeeting

Ohphone

Chiamata non a buon fine

Test 3 : Tiptel (Innovaphone) ß -à Tiptel (Innovaphone)

 

 

Chiamante

Chiamato

Esito

Tiptel

Tiptel

Tutto OK!

Tiptel

Tiptel

Tutto OK!

 

Test 4 : Tiptel (Innovaphone) ß -à Netmeeting (Windows)

 

 

Chiamante

Chiamato

Esito

Tiptel

Netmeeting

Tutto OK!

Netmeeting

Tiptel

Tutto OK!

 

Test 5 : Netmeeting (Windows) ß -à Netmeeting (Windows)

 

Chiamante

Chiamato

Esito

Netmeeting

Netmeeting

Tutto OK!

Netmeeting

Netmeeting

Tutto OK!

 

Test 6: Ohphone (Linux)ß -à Ohphone (Linux)

 

 

 

Chiamante

Chiamato

Esito

Ohphone

Ohphone

Tutto OK!

Ohphone

Ohphone

Tutto OK!





Page hosted at Laboratorio Telematico at Info-Com
Last Update ven gen 31 18:03:32 CET 2003