Capitolo 3
Networking in Linux : routing e gestione delle risorse di rete
3.1 Intoduzione
Buona parte dei router e dei server mondiali su cui si basa la rete lavorano supportando il sistema operativo Linux. Questo offre un ricco insieme di funzioni per la gestione di reti in tutti i loro aspetti. Ciò è dovuto in gran parte alla natura open source di questo sistema operativo che ha permesso a chiunque di sviluppare liberamente delle parti del codice sorgente allo scopo di risolvere un problema pratico. Sebbene sia difficile in un ambiente così strutturato definire precisamente i meriti da assegnare a ciascuno, una menzione particolare va sicuramente a Alexey N. Kuznetsov dell' Istituto per la Ricerca Nucleare di Mosca il quale è il cretore della struttura del kernel, attualmente fornita standard con le varie distribuzioni di Linux, per il networking.
Nella prima parete di questo capitolo cercherò di dare una descrizione chiara di questa architettura interna passando altresì in rassegna gli strumenti disponibili da me individuati come utili al fine del raggiungimento del nostro scopo.
Infine vedremo come è stato raggiunto l'obiettivo del controllo dell'utilizzo della banda nel corso di una sessione H.323 compatibilmente con inderogabili esigenze di qualità del servizio.
3.2 La struttura del kernel e i comandi per il controllo del traffico.
Linux offre un ricco set di funzioni per il controllo del traffico come i comandi tc , iptables , iproute e così via i quali si appoggiano su una struttura definita nel kernel del sistema operativo. Cerchiamo di definire in che modo vengono elaborati ipacchetti all'interno delle macchine linux. A livello più alto possibile possiamo immaginare una situazione come quella di figura:

Fig
3.1
La figura mostra come il kernel processi i dati ricevuti dalla rete e come esso possa generare nuovi dati che dovranno essere spediti sulla rete : i pacchetti entranti possono sia essere reindirizzati immediatamente sulla rete ( magari su diverese interfacce nel caso di un router ), sia essere passati al livello superiore della pila protocollare ( come ad esempio un protocollo di trasporto ) per essere processati.
Il Forwarding include la selezione dell'interfaccia di uscita , la selezione del prossimo salto, l'incapsulamento e così via. Una volta fatto questo i pacchetti sono accodati sulle rispettive interfacce dove vigeranno le discipline di accodamento per la gestione del traffico.
3.2.1 Iproute2
Volendo esplorare più in profondità il problema cominciamo con l'analizzare il blocco denominato input de-multiplexing. Al suo interno possiamo immaginare la routing table relativa ai pacchetti provenienti dall' esterno e che devono essere reindirizzati . Il pacchetto per la gestione dell' indirizzamento è iproute2.
Questo risulta essre uno strumento molto flessibile e completo in quanto è possibile definire più tavole di routing da consultare a seconda della sorgente del pacchetto, della destinazione o semplicemente dell'utente che richiede l'utilizzazione della risorsa. Tra la miriade di comandi disponibili quelli di cui ha più senso parlare in questa sede sono:
ip route : aggiunge o cancella una riga della routing table in cui sono contenute le informazioni sui percorsi per raggiungere gli altri nodi di rete.
ip rule : definisce delle regole di instardamento non dipendenti solo dalla sorgente e dall estinazione del pacchetto ma anche da altri campi dell'header. Per fare ciò definise più routing table a seconda del tipo di pacchetto.Ma le diverse routing table dovranno comunque essere gestite tramite ip route.
ip link : setta le caratteristiche dei dispositivi di uscita ( schede di rete , modem ).
ip address : associa indirizzi ip ai dispositivi di rete.
Per rendere più chiaro quanto detto vediamo come sono le impostazioni su genni:
IMPOSTAZIONI DI ROUTING DI GENNI
#ip link list
[alec@genni alec]$ ip link list
1: lo: <LOOPBACK,UP> mtu 16436 qdisc noqueue
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0: <BROADCAST,MULTICAST,PROMISC,UP> mtu 1500 qdisc p_fifo_fast
qlen 100
link/ether 00:01:02:15:8b:e5 brd ff:ff:ff:ff:ff:ff
Come possiamo veder su genni sono definite 2 interfacce tramite le quali fluisce il traffico:
un'interfaccia locale con mtu ( maximum transfert unit in ottetti ) di 16436 senza alcuna disciplina di coda definita
l'interfaccia di rete eth0 con mtu 1500 e una disciplina di coda che di default è una fifo di lunghezza 100.
Per vedere gli indirizzi assegnati ad ogni interfaccia usiamo ip address .
[alec@genni alec]$ ip address
1: lo: <LOOPBACK,UP> mtu 16436 qdisc noqueue
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 brd 127.255.255.255 scope host lo
2: eth0: <BROADCAST,MULTICAST,PROMISC,UP> mtu 1500 qdisc p_fifo_fast
qlen 100
link/ether 00:01:02:15:8b:e5 brd ff:ff:ff:ff:ff:ff
inet 151.100.11.28/24 brd 151.100.11.255 scope global eth0
Il dispositivo locale è noto con l'indirizzo 127.0.0.1/8. Il /8 finale indica che sono libero di assegnare gli ultimi 32 - 8 = 24 bit come più mi piace, così come indicato dalla netmask.
L'indirizzo associato alla scheda di rete è quello ip della macchina 127.100.11.28 e il /24 indica che la nella sottorete a cui appartengo sono libero di sfruttare per l'indirizzamento gli ulyimi 8 bit.
Passando a ip rule vediamo che come standard sono definite 3 routing table .
#ip rule list
[alec@genni alec]$ ip rule list
0: from all lookup local
32766: from all lookup main
32767: from all lookup 253
Quella che conosciamo di solito e la main che definisce l'instradamento dei pacchetti in funzione della loro destinazione.
#ip route show (route -n)
[alec@genni alec]$ route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
151.100.11.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0
151.100.8.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0
151.100.9.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0
127.0.0.0 0.0.0.0 255.0.0.0 U 0 0 0 lo
151.100.11.1 0.0.0.0 UG 0 0 0 eth0
In questo caso ,non essendo genni un router ,la situazione è molto semplice. Devo solo specificare che, a meno che il pacchetto non sia per me ,qualunque pacchetto lo giro al Gateway di facoltà 151.100.11.1. In raltà per velocizzare la rete e non appesantire di carico inutile il Gateway ho anche specificato che qualunque host del tipo 151.100.11.0/32 , 151.100.8.0/32 e 151.100.9.0/32 è direttamente raggiungibili.
La local table serve solo a specificare cose ovvie relative al traffico interno alla macchina
che però da qualche parte vanno pure specificate. L'ultima routing table 253 e quella di defoult , ultima ad essere guardata, che interviene se un pacchetto si perde non essendo selezionato da alcuna tavola.
[alec@genni alec]$ ip route list table local
local 151.100.11.28 dev eth0 proto kernel scope host src 151.100.11.28
broadcast 127.255.255.255 dev lo proto kernel scope link src 127.0.0.1
broadcast 151.100.11.255 dev eth0 proto kernel scope link src 151.100.11.28
broadcast 151.100.11.0 dev eth0 proto kernel scope link src 151.100.11.28
broadcast 127.0.0.0 dev lo proto kernel scope link src 127.0.0.1
local 127.0.0.1 dev lo proto kernel scope host src 127.0.0.1
local 127.0.0.0/8 dev lo proto kernel scope host src 127.0.0.1
La cosa interessante è che io posso specificare, tramite il comando ip rule, altre routing table che non dipendano solo dall'indirizzo di destinazione ma magari dalla sorgente del traffico. Per fare un esempio pratico se io ho più utenti collegati ad un router e voglio ad esempio che solo alcuni possano usufruire di alcune interfacce di uscita del router particolarmente veloci posso definire delle routing table personalizzate per gli utenti. Nelle routing table degi utenti meno pregiati non compariranno come possibili interfacce di ucita quelle più veloci.
Visto che la modifica della routing table non è utile al nostro scopo rimandiamo per maggiori approfondimenti al manuale di iproute2[].
3.2.2 Traffic controller
Vediamo di analizzare ora la parte e di gestione della coda di uscita. Volendo comprendere più in profondità il controllo di traffico cominciamo ad introdurre i concetti chiave tramite i quali il kernel di Linux ragiona:
Queuing discipline
A ogni dispositivo di rete può essere associata una disciplina di accodamento che effettua uno scheduling dei pacchetti in ingresso . Di default questa disciplina è una semplice FIFO.
Classes
Le classi servono a definire le varie tipologie di traffico.Ogni classe e padrona di una coda la quale di default è una FIFO. Quando la disciplina di accodamento è chiamata questa applica i filtri per determinare la classe alla quale il pacchetto appartiene .
Filters
I filtri si occupano di smistare il traffico nelle diverse classi . Il concetto di filtro è molto più libero di quella di classe , infatti più filtri possono essere associati alla stessa classe e quindi alla stessa coda.

Fig
3.2
Ora spiegato a grandi linee qual' è la struttura con la quale abbiamo a che fare andiamo a vedere i comandi con cui realizzarla e le funzionalità a cui possiamo attingere.
|
ELEMENTO |
COMANDI |
|---|---|
|
Queuing discipline |
tc qdisc ...(OPTIONS) |
|
Class |
tc class ...(OPTIONS) |
|
Filter |
tc filter ... (OPTIONS) |
La fonte più precisa di informazione sul traffic control sono proprio gli help in linea , usiamoli quindi come punto di partenza per capire i comandi e le possibili opzioni.
3.2.2.1 tc qdisc
[alec@genni alec]$ tc qdisc help
Usage: tc qdisc [ add | del | replace | change | get ] dev STRING
[ handle QHANDLE ] [ root | ingress | parent CLASSID ]
[ estimator INTERVAL TIME_CONSTANT ]
[ [ QDISC_KIND ] [ help | OPTIONS ] ]
tc qdisc show [ dev STRING ] [ingress]
Where:
QDISC_KIND := { [p|b]fifo | tbf | prio | cbq | red | etc. }
OPTIONS := ... try tc qdisc add <desired QDISC_KIND> help
E' possibile creare, cancellare, rimpiazzare o semplicemente cambiare qualche parametro a una disciplina di accodamento ( qdisc ). Subito dopo devo specificare a quale device di uscita andarà applicata questa disiplina . Prima di spiegare le altre opzioni precisiamo che non è obbligatorio lavorare con le classi. Le classi sono un arnese molto potente e flessibile ma anche piuttosto complicato. Il più delle volte è possibile utilizzando filtri e qdisc riuscire comunque a raggiungere il nostro scopo.
Poniamo di voler semplicemente limitare la banda in uscita da eth0 , dirò semplicemente :
#tc qdisc add dev eth0 root tbf rate 220kbit latency 50ms burst 1540
questo comando applica una disciplina token buckets a eth0 con certi parametri di banda.
Le più importanti qdisc classless ( che funzionano senza dover definire delle classi) sono:
TBF ( Token Buckets Flow ) Questa struttura di coda è immaginabile come una secchio con un buco sul fondo per cui il flusso in uscita tende ad essere costante (a meno che il secchio non sia vuoto). Quando viene riempito troppo velocemente si riempi e l'acqua in eccesso cade ai bordi e non è piu recuperabile. Allo stesso modo possiamo definire la dimensione massima della coda e il suo rate di uscita . Usiamo sempre l'help in linea per vedere l'esatta struttura del comando.
Usage: ... tbf limit BYTES burst BYTES[/BYTES] rate KBPS [ mtu BYTES[/BYTES] ]
[ peakrate KBPS ] [ latency TIME ]
burst/buffer/maxburst: indica la dimensione del secchio in bytes.
limit or latency: con la latenza indico il tempo massimo che un pacchetto può restare all'interno della coda mentre con limit indico il numero di byte che possono restare in attesa di un gettone disponibile.
rate : indica quanti gettoni al secondo ho a disposizione, è il rate massimo di trasmissione.
mpu: pacchetti al id sotto di questa dimensione non vengono accodati.
peackrate: Se un gettone è disponibile e dei pacchetti arrivano, sono spediti immediatamente . Questa potrebbe non essere la tua volontà spece se hai un grosso secchio. Il peackrate indica al massimo quanto rapidamente vuoi che si svuoti il secchio.
mtu/minburst: sprcifica la dimensione del secchio di peackrate.
FIFO (First In First Out)
Come indica il nome stesso i pacchetti non ricevono alcun specifico trattamento: il promo ad arrivare è anche il primoad essere servito.Questa coda ha però tre cosiddette bande nelle quali si applica la disciplina fifo.Se ci sono pacchetti da processare nella banda 0 quelli nella bqnda 1 non vengono presi in considerazione. IL kernel suddivide i diversi tipi di traffico attraverso la lettura nell'header del pacchetto del campo TOS ( Type Of Servivce). Il campo TOS è costituito di 8 bit dei quali solo 4 utilizzati ( MBZ=maust be zero, PRECEDENCE non è usato). Le 16 configurazioni possibili definiscono 16 QoS . In realtà se ne sfuttano solo 4 :
0 1 2 3 4 5 6 7
+-----+-----+-----+-----+-----+-----+-----+-----+
| PRECEDENCE | TOS | MBZ |
+-----+-----+-----+-----+-----+-----+-----+-----+
Binary Decimal Meaning Band
----------------------------------------------------------------
1000 8 Minimize delay 0
0100 4 Maximize throughput 2
0010 2 Maximizerealibility 1
0001 1 Minimize monetary cost 2
0000 0 Normal Service 1
Fig. 3.3
Ad ogni tipo di servizio è associata una delle tre bande. Questa asociazione definisce la priomap( tabella di priorità).
Usage: ... [p|b]fifo [ limit NUMBER ]
SFQ (Stocastic Fairness Queuing)
E' una semplice implementazione, meno accurata , ma anche molto leggera come calcolo.
Individua ogni sessione TCP o UDP. Quindi divide stocasticamente il traffico in flussi e ad ogni flusso viene assegnata una qdisc FIFO alla quale a turno viene data la possibiltà di trasmettere.
Usage: ... sfq [ perturb SECS ] [ quantum BYTES ]
perturb: parametro per l'algoritmo di confusione (tempo di miscelazione)
quantum: numero di bytes trasmittibili prima di cedere la mano
Questa qdisc è da usare solo se l'interfaccia di uscita è veramente satura altrimenti non darà alcun effetto.
RED (random Early Detection)
Una coda Red può essere vista come una variante di quella FIFO in quanto in uscita i pacchetti sono spediti allo stesso modo mentre ciò che varia è il meccanismo di accodamento. Innanzi tutto bisogna definire due soglie una minima e una massima. Per capire come lavorano aiutiamoci con un grafico:

Fig.
3.4
Quando la coda ha una lunghezza maggiore o uguale della soglia massima qualunque pacchetto che arriva viene scartato, mentre quando ci si trova tra le due soglie inizia uno scarto casuale dei pacchetti in arrivo con una probabilità crescente tanto più la coda si riempie.
Usage: ... red limit BYTES min BYTES max BYTES avpkt BYTES burst PACKETS
probability PROBABILITY bandwidth KBPS [ ecn ]
3.2.2.2 tc class
Anche per l'elemento classe facciamo uso dell'help in linea :
[alec@genni alec]$ tc class help
Usage: tc class [ add | del | change | get ] dev STRING
[ classid CLASSID ] [ root | parent CLASSID ]
[ [ QDISC_KIND ] [ help | OPTIONS ] ]
tc class show [ dev STRING ] [ root | parent CLASSID ]
Where:
QDISC_KIND := { prio | cbq | etc. }
OPTIONS := ... try tc class add <desired QDISC_KIND> help
Il comando ha una struttura simile a quella di tc qdisc. Possiamo creare, cancellare o cambiare i parametri ad una clase esistente; dobbiamo specificare il device a cui associamo la classe; quindi dobbiamo definire l'albero gerarchico delle classi . Infatti ogni qual volta lavoriamo con le classi definiamo un albero che ad ogni ramo associa un flusso di traffico. I rami vanno denominati tramite una dicitura x:y del tipo:

Fig.
3.5
Le classi definibili sono:
PRIO (priority)
Questa opzione definisce più classi a diversa priorità che verranno servite in ordine decrescente di quest'ultima. In effetti la disciplina PRIO e un ibrido tra le discipline classless e classfull . Viene infatti invocata con il comando tc qdisc e non con quello tc class ma il suo effetto e di dividere il traffico in un certo numero di bande (di default 3) a diverse priorità le quali si comportano a tutti gli effetti come classi. Sui rami però possiamo definire delle discipline classless ( TBF, RED....). Di default sui rami ho delle FIFO che seguono semplicemente le priorità introdotte dal campo TOS.
In effetti ci sono diverse analogie tra la qdisc FIFO e la PRIO ma la differenza sostanziale è che la priomap della FIFO e fissa , così come le bande , mentre nella PRIO sono definibili sia il numero di bande sia la priomap.Infatti come leggiamo nell' help:
Usage: ... prio bands NUMBER priomap P1 P2..
Come parametri posso dare il numero di bande da creare e se voglio una priomap da me definita per la FIFO ( a meno che non definisco altre qdisc e applico dei filtri per associare un certo flusso ai rami). Comunque devo stare attento che gli alberi con priorità più alta (e quindi meno serviti ) non soffrano di starving ( muoiano di fame).
CBQ ( class-based queuing)
E' la classe più completa disponibile. Permette di sagomare perfettamente il tipo di traffico che dovrà trattare . Questa qualità si paga con una complessità che è di gran lunga maggiore delle altre qdisc . CBQ è anche lui uno shaper del traffico anche se sotto quetso aspetto il suo funzionamento e di volta in volta è da mettere alla prova. Dovrebbe lavorare così: se abbiamo una banda disponibile d'uscita di 10mbit/s che vogliamo limitare a 1 mbit/s allora lo shaper CBQ lavora in modo tale da lasciare all'interfacia d'uscita libera per il 90% del tempo.
Quindi CBQ lavora in modo tale da essere sicura che il collegamento risulti libero per un tempo opportuno tanto da abbattere il rate trasmissivo fino alla limitazione configurata. Per fare ciò essa calcola il tempo medio,avgidle , che dovrebbe passare tra i pacchetti. Durante le operazioni questo tempo è misurato usando una media mobile esponenziale (EWMA) che considera più importante l'informazione relativa ai pacchetti più recenti. Se il tempo d'arrivo è maggiore di quanto calcolato il collegamento accumula crediti trasmissivi fino ad un tetto massimo mentre se questo tempo d'arrivo previsto è minore la CBQ chiude i battenti per un pò essendo in sovraccarico. In questa situazione la CBQ dovrebbe stozzare il canale per un tempo pari al avgidle calcolato quindi fare passare un pacchetto e richiudelo nuovamente. In realtà questo comportamento è gestito dal parametro maxburst. Andiamo allora avedere i parametri settabili.
Usage: ... cbq bandwidth BPS rate BPS maxburst PKTS [ avpkt BYTES ]
[ minburst PKTS ] [ bounded ] [ isolated ]
[ allot BYTES ] [ mpu BYTES ] [ weight RATE ]
[ prio NUMBER ] [ cell BYTES ] [ ewma LOG ]
[ estimator INTERVAL TIME_CONSTANT ]
[ split CLASSID ] [ defmap MASK/CHANGE ]
avpkt : dimensione media dei pacchetti espressa in bytes.
bandwidth : la banda fisica della interfaccia.
maxburst : questo numero di pacchetti è usato per calcolare il massimo tempo prima che avgidle crolli a zero. Più e alto più CBQ risulta tollerante ai burst di pacchetti.
minburst: CBQ necessita di strozzare il canale trasmissivo in caso di overlimit. Quando passa avgidle il canale viene riaperto e passa un pacchetto o più a seconda di quanto qui specificato. Più pacchetti passano, più tempo dovrà passare per una riapertura del canale.
minidle: se avgidle è un numero negativo siamo in overlimits ebisogna aspettare fino a che non cresce fino a rendere possibile la spedizione di un pacchetto. Qui si definisce il valore limite , in basso, che può asssumere avgidle.Comunque è un numero negativo quindi 10ms significa -10ms.
mpu: dimensione minima dei pacchetti.
rate: rate trasmissivo desiderato.
isolated: si specifica che una classe non può ne prestare ne prendere in prestito banda.
bounded: si specifica che una classe non può chiedere in prestito banda.
Prima di fare un esempio sull'uso delle classi vediamo anche i filtri.
3.2.2.3 tc filter
[alec@genni alec]$ tc filter help
Usage: tc filter [ add | del | change | get ] dev STRING
[ pref PRIO ] [ protocol PROTO ]
[ estimator INTERVAL TIME_CONSTANT ]
[ root | classid CLASSID ] [ handle FILTERID ]
[ [ FILTER_TYPE ] [ help | OPTIONS ] ]
tc filter show [ dev STRING ] [ root | parent CLASSID ]
Where:
FILTER_TYPE := { rsvp | u32 | fw | route | etc. }
FILTERID := ... format depends on classifier, see there
OPTIONS := ... try tc filter add <desired FILTER_KIND> help
I filtri sono gli oggetti che effettivamente smistano il traffico. Sono in grado di guardare dentro il pacchetto il campo dell'header che contiene le informazioni discriminatorie e di associare al pacchetto la classe corrispondente o semplicemente il ramo corrispondente x:y di un albero.
I più importanti criteri discriminanti sono:
fw
Basa la sua decisione su come il firewall ha marchiato i pacchetti.
Usage: ... fw [ classid CLASSID ] [ police POLICE_SPEC ]
POLICE_SPEC := ... look at TBF
CLASSID := X:Y
Devo semplicemente specificare il marchio da riconoscere e il ramo a cui associarlo.
# tc filter add dev eth0 protocol ip parent 1:0 prio 1 handle 1 fw classid 1:11
u32
Basa la sua decisione sul confonto con un ben specificato campo allinterno dell'header ip del pacchetto.
Usage: ... u32 [ match SELECTOR ... ] [ link HTID ] [ classid CLASSID ]
[ police POLICE_SPEC ] [ offset OFFSET_SPEC ]
[ ht HTID ] [ hashkey HASHKEY_SPEC ]
[ sample SAMPLE ]
or u32 divisor DIVISOR
Where: SELECTOR := SAMPLE SAMPLE ...
SAMPLE := { ip | ip6 | udp | tcp | icmp | u{32|16|8} } SAMPLE_ARGS
FILTERID := X:Y:Z
Il funzionamento di questo discriminatore non è subito intuitivo e l'help in questo caso non aiuta a mettere chiarezza. Per cominciare ripassiamoci la struttura dell'header ip:

Fig. 3.6
Io posso , tramite una maschera , specificare un determinato campo dell'header ed il valore che devo confrontare. Ad esempio se voglio andare a guardare nel campo TOS specificherò gli otto bit corrispondenti in esadecimale come 00ff0000 at 0 (sulla riga 0 ) ed il valore da confrontare. Vediamo un esempio di comando:
# tc filter add eth0 protocol ip parent 1:0 pref 10 u32 \
match u32 00100000 00ff0000 at 0 flowid 1:0
A parte la prima riga la parola chiave è match. Viene specificato di confrontare un valore di riferimento di 32 bit in esadecimale 00100000 con quelli del pacchetto corrispondenti alla maschera 00ff0000 sulla prima riga dell'header così come schematizzato sopra . Se il confronto ha esito positivo si associa il pacchetto al ramo 1:0.
route
Basato sulle decisioni della routing table sulla destinazione del pacchetto.
Usage: ... route [ from REALM | fromif TAG ] [ to REALM ]
[ flowid CLASSID ] [ police POLICE_SPEC ]
POLICE_SPEC := ... look at TBF
CLASSID := X:Y
rsvp
Usage: ... rsvp ipproto PROTOCOL session DST[/PORT | GPI ]
[ sender SRC[/PORT | GPI ]
[ classid CLASSID ] [ police POLICE_SPEC ]
[ tunnelid ID ] [ tunnel ID skip NUMBER ]
Where: GPI := { flowlabel NUMBER | spi/ah SPI | spi/esp SPI |
u{8|16|32} NUMBER mask MASK at OFFSET}
POLICE_SPEC := ... look at TBF
FILTERID := X:Y
Esempio di tc
Poniamo di voler suddividere la banda a disposizione (10Mbit) della mia macchina (che poniamo funga anche da router ) come schematizzato:
10% della banda (1Mbit) assegnata al traffico web
30% della banda (3Mbit) assegnata al traffico telnet
60% della banda (6Mbit) assegnata al traffico rimanente
Aggiungiamo lo acheduler delle classi CBQ all'interfacia eth0:
# tc qdisc add dev eth0 root handle1: cbq bandwhidth 10Mbit avpkt 1000
quindi creiamo l'albero delle classi .Prima la classe radice:
# tc class add dev eth0 parent 1:0 classid 1:10 cbq bandwidth 10Mbit rate 10Mbit allot 1514 weight 1Mbit prio 8 maxburst 20 avpkt 1000
Quindi le sottoclassi:
# tc class add dev eth0 parent 1:10 classid 1:11 cbq bandwidth 10Mbit rate 1Mbit allot 1514 weight 100kbit prio 5 maxburst 2o avpkt 1000
# tc class add dev eth0 parent 1:10 classid 1:12 cbq bandwidth 10Mbit rate 3Mbit allot 1514 weight 100kbit prio 5 maxburst 2o avpkt 1000
# tc class add dev eth0 parent 1:10 classid 1:13 cbq bandwidth 10Mbit rate 6Mbit allot 1514 weight 100kbit prio 5 maxburst 2o avpkt 1000

Fig. 3.7
Ora dobbiamo associare le tre tipologie di traffico alle tre classi create. Dobbiamo quindi dichiarare tre filtri , uno per ogni flusso, che catturino i pacchetti che gli competono. I filtri come abbiamo visto guardano alcuni campi dell'header settati opportunamente . Se per esempio poniamo che sia stato settato il campo fw a 1 per il traffico http ,a 2 per il telnet e 3 per il resto i filtri dovranno così essere dichiarati:
# tc filter add dev eth0 protocol ip parent 1:0 prio 1 handle 1 fw classid 1:11
# tc filter add dev eth0 protocol ip parent 1:0 prio 1 handle 2 fw classid 1:12
# tc filter add dev eth0 protocol ip parent 1:0 prio 1 handle 3 fw classid 1:13
La prima istruzione ad esmpio ci dice che i pacchetti marchiati 1 fw vengono associati alla classe 1:11.
Ora la questione aperta è capire come possiamo intervenire per marchiare i pacchetti o come intervenire sui campi dell'header per essere parte attiva nella definizione della QoS. Con questo scopo andiamo a conoscere il comando iptables.
Iptables
Iptables è usato per settare, gestire, e ispezionare le tavole delle regole dei filtri per i pacchetti IP nel Kernel di Linux. Possono essre definite diverse tavole. Ogni tavola contiene una serie di regole detta catena in quanto le regole vengono controllate una ad una seguendo un certo ordine e la prima che combacia col pacchetto viene selezionata.

Fig .
3.8
Gli obiettivi a cui raggiungere una volta selezionato il pacchetto possono essere ad esempio l'accettazione del pacchetto (ACCEPT), la sua distruzione (DROP) o anche il marchiare il pacchetto (MARK) o la ridefinizione del campo TOS nell'header.
Iptables ha 4 catene predefinite che possono essere settate a nostro piacimento ma mai essere cancellate del tutto. Queste sono le catene di INPUT, di OUTPUT, di PREROUTING edi FORWARD. Cerchiamo di definire la loro posizione capendo in che ordine vengano consultate dal Kernel.

Fig. 3.9
La catena di PREROUTING interviene sul pacchetto prima che questo venga analizzato dalla routing table . Altrimenti i pacchetti vengono normalmente analizzati prima di essere reindirizzati sulla queuing discipline o prima di entrare negli strati superiori in locale.
Ora vista la struttura generale vediamo a grandi linee i possibili comandi definibili .
Comandi iptables
TABLES
-t , --table
Ci sono tre tavole indipendenti: filter, nat, mangle. La filer table è quella di default e contiene le catene di input di INPUT e di OUTPUT mentre la mangle table (usata per l'alterazione dei pacchetti) contiene quelle di OUTPUT e di PREROUTING. La nat table invece è consultata quando è incontrato un pacchetto che crea una nuova connessione e consiste in tre catene di PREROUTING, OUTPUT e POSTROUTING ( per alterare i pachetti che stanno per uscire).
COMANDI
-A , --append : aggiungi una regola alla fine catena
-D , --delete : cancella una regola da una catena
-R , --replace : sostituisci una regola
-I , --insert : inserisciuna regola in una catena ad una certa posizione
-L , --list : elenca le regole
-X , --delete-chain : cancella una catena
-F , --flush : cancella tutte le regole di una catena
-N , --new-chain : crea una nuova catena
-P , --policy : setta la politica della catena
-E , --rename-chain : rinomina la catena
PARAMETRI
I seguenti parametri definiscono i parametri di una regola da creare o aggiungere.
-p , --protocol : definisce il protocollo, può essere tcp, udp, icmp e così via
-s , --source : definisce la sorgente del pacchetto , può essere un indirizzo ip
ma anche un idirizzo di rete o il nome di un host.
-d , --destination : definisce la destinazione del pacchetto
-j , --jump : definisce il TARGET della regola
-i , --in-interface : definisce l'interfaccia di ingresso
-o , --out-interface : definisce l'interfaccia su cui verra spedito il pacchetto
MATCH EXTENSIONS
Devo specificare che sto inserendo delle estensioni per le opzioni di confronto.
Per fare ciò uso :
-m, --match
quindi specifico che tipo di estensione uso:
tcp o udp
--source-port : specifica le porte sorgenti dei pacchetti ( Se si è definito tra i le opzioni il
protocollo tcp o udp -m è sottointeso )
--destination-port : specifica le porte di destinazione dei pacchetti ( Se si è definito tra i
le opzioni il protocollo tcp o udp -m è sottointeso)
multiport
--source-port [port,port,..] : specifica le porte sorgenti dei pacchetti
--destination-port [port,port,..] : specifica le porte di destinazione dei pacchetti
--port [port,port,..] : le porte specificate lo sono sia in sorgente che in destinazione
esempio: ... -m multiport port 3456,4563,2348 ....
mark
--mark : confronta il marchio contenuto nel pacchetto
tos
--tos value : confonta il campo tos con quello dell'header del pacchetto.
Esempio : ....-m tos --tos 0x10 .....
TARGET EXTENSIONS
Oltre ai target predefiniti come ACCEPT o DROP con iptables sono definibili degli altri obiettivi da specificare con la sintassi : ...-j TARGET-EXTENSIONS options :
MARK --set-mark value : marchia i pacchetti
REJECT --reject-with type : rispedisce indietro i pacchetti co un certo tipo di errore come
icmp-net(host,port)-unreachable(prohibided)
TOS --set-tos value : setta gli otto bit del capo TOS
Esempio con iptables
Possiamo ora concludere l'esempio precedente facendo in modo che i pacchetti appartenenti ai tre tipo di traffico (web,telnet,altro) vengano marchiati con tre valori differenti in modo tale da essere riconosciuti dal tc filter fw definito.
# iptable -A FORWARD -p tcp -d 10.0.1.0 destination-port 23 -j ACCEPT MARK
--set-mark 1
# iptable -A FORWARD -p tcp -d 10.0.1.0 destination-port 80 -j ACCEPT MARK
--set-mark 1
# iptable -A FORWARD -p tcp -d 10.0.1.0 -j ACCEPT MARK --set-mark 1
In questo modo i pacchetti che transitano dall'esterno verso la routing table vengono reindirizzati. Quelli in transito a seconda del tipo vengono marchiati nella catena di FORWARD da iptables che funge da firewall. La queuing discipline seguente riconosce i tre marchi e tramite questi identifica i tre tipi di traffico trattandoli nei modi dovuti ad ognuno.

Fig.
3.10
Soluzioni implementate e relativi test
A seguito dello studio delle potenzialità del Networking messe a disposizione dal kernel di Linux cerchiamo di definire quali siano le nostre necessità in modo da progettare ed in seguito testare un controllo del traffico appropriato.
Come primo parametro implementativo abbiamo le tipologie di traffico che dovremo trattare.
L'host genni oltre a fornire i servizi H.323 dovrà anche continuare a offrire tutte le funzionalità di un normale PC. Dovrà quindi continuare ad interfacciarsi alla rete di ateneo essendo in grado di mandare e ricevere e-mail (protocollo smtp) o procedere ad un download ftp, oltre ovviamente al normale traffico TCP/IP. Consideriamo che per queste tipologie di traffico il comportamento dell'host genni non debba risentire in alcun modo dello shaping del traffico in uscita che noi andremo ad imporre. Ho deciso quindi dividere il traffico in due flussi separati, uno dei quali individui in qualche modo ( TOS, porte , protocollo) il traffico di videoconferenza, mentre l'altro individui tutto il resto del traffico che dovrà essere trattato con le stesse priorità individuate solitamente tramite il campo TOS (Type Of Service).
Come secondo fondamentale parametro dovremo definire la banda massima da assegnare ai servizi H.323 ed eventualmente se ci interessa anche quella da assegnare al restante traffico. Questo parametro dovrà essere definito a seguito di test sulla qualità del servizio di videoconferenza risultante da diverse limitazioni di banda.
Molti altri parametri occorre definire al momento della dichiarazione del comando. Come abbiamo visto ci sono parametri legati alla tipologia dello shaper . Ad esempio nella definizione di una qdisc TBF dobbiamo definire, oltre al rate con cui vengono rilasciati i gettoni, anche la dimensione del secchio e il tempo di latenza massimo all'interno della coda.
Le architetture di gestione del traffico che ho considerato come più opportune per la realizzazione del nostro scopo prevedono sia qdisc classless che qdisc classbased. La prima è costutuita da uno scheduler PRIO con a valle due TBF, mentre la seconda prevede la definizione di due classi distinte caratterizzate al meglio grazie ai molto parametri previsti dalle qdisc CBQ.
3.3.1 La qdisc classless: PRIO + TBF
Lo scheduler PRIO, come visto nei paragrafi precedenti di questo capitolo, di default si comporta in pratica come una semplice FIFO. Divide il traffico in tre bande 0,1,2 che vengono servite in ordine crescente. La 1 viene servita solo quando la coda 0 e vuota e la 2 a sua volta viene servita solo quando la 2 è vuota. Questa priorita è definita dalla priomap che si basa sul campo TOS definito all'interno dell'header IP di ogni pacchetto. La differenza è che nel caso della qdisc PRIO noi possiamo modificare queta priomap o semplicemente trascurarla indirizzando il traffico sulle bande attrverso la definizione di filtri associati alle bande. Nel nostro caso possiamo associare i due flussi di traffico a due bande della disciplina PRIO. In questo modo però il traffico non è ancora limitato ma solo smistato.
Per limitarlo occorre definire due code d'uscita associate alle due bande. Tramite una TBF fissiamo quindi una rate massimo per ogni banda.
Andiamo a vedere passo passo le istruzioni da assegnare per la definizione dello shaper.
Per prima cosa dobbiamo essere sicuri che non siano definiti altre qdisc o filtri:
[root@genni alec]# iptables -L -t mangle
Chain PREROUTING (policy ACCEPT)
target prot opt source destination
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
così ci accertiamo che non sia settato alcun firewall in uscita da genni.
Vediamo se è presente qualche qdisc:
[root@genni alec]#tc -d qdisc show dev eth0
[root@genni alec]#
abbiamo specificato di accertarci dell'assenza di qdisc sull'interfaccia di rete eth0 che è l'unica presente in genni, altrimenti avremo dovuto controllare anche le altre.
Effuttuiamo lo stesso controllo anche su classi e filtri :
[alec@genni alec]$ tc -d class show dev eth0
[alec@genni alec]$
[alec@genni alec]$ tc -d filter show dev eth0
[alec@genni alec]$
In realtà questo controllo è superfluo in quanto non possono esistere classi e filtri appesi nel nulla. Questi devono essere sempre associati ad uno scheduler e se questo viene cancellato vengono cancellati anche i filtri e le classi ad esso collegate.
Come primo passo definiamo i due flussi colorandoli tramite un marchio che il firewal appone sui paccchetti e che ha senso solo all'interno dell'host. Usiamo iptables:
[root@genni alec]# iptables -A OUTPUT -t mangle -s 151.100.11.28 -j MARK --set-mark 2
[root@genni alec]# iptables -A OUTPUT -t mangle -p udp -s 151.100.11.28 -m tos --tos 0x10 -j MARK --set-mark 1
Abbiamo deciso di individuare il traffico tramite il protocollo UDP e il campo TOS che nel caso dell'H.323 è il classico delle comunicazioni in tempo reale: Minimize Delay (0x10). Mentre tutto il resto del traffico viene definito genericamente come tutto il traffico proveniente dall'host 151.100.11.28 cioè genni. Per specificare meglio il traffico si potrebbe anche imporre la condizione sulle porte usate dall'openmcu e dai client H.323 in genere, cioè le porte 5000-5003. Si è ottenuto un medesimo risultato ma è più sensibile a eventuali richieste sballate o volutamente modificate in fase di instaurazione della chiamata da parte di un utente. La soluzione da me scelta risulta essere più conservativa anche se meno chirurgica.
Particolare attenzione va messa nell'ordine in cui si danno le regole che vanno apposte dalle più generali a quelle più particolari. Quindi va prima quella che seleziona tutto il traffico e poi quella che colora il traffico UDP con campo TOS 0x10. Questo perchè le regole del firewall non funzionano come dei filtri deviatori di traffico ma come dei veri e propri ostacoli al passaggio dei pacchetti. Nelle due regole definite è sottintesa la politica ACCEPT che fa passare oltre i pacchetti con la condizione di marchiarli. Se vessi usato L'opzione DROP i pacchetti sarebbero sati scartati. Il pacchetto arrivando incontra da principio la prima regola che colora indifferentemente ogni pacchetto marchiandolo 1 quindi arriva al secondo muro che marchia il traffico UDP con TOS 0x10 con 2, mentre lascia invariato il marchio degli altri. Se avessi invertito l'ordine comunque tutti i pacchetti sarebbero stati marchiati 2.
Chiusa questa parentesi, che mi ha fatto perdere a suo tempo due giorni di lavoro, controlliamo se le modifiche da noi apportate sono andate a buon fine:
[root@genni alec]# iptables -L -t mangle
Chain PREROUTING (policy ACCEPT)
target prot opt source destination
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
MARK all -- genni.ing.uniroma1.it anywhere MARK set 0x2
MARK udp -- genni.ing.uniroma1.it anywhere TOS match Minimize-Delay MARK set 0x1
Ora andiamo a definire la qdisc. Per prima cosa lo scheduler PRIO :
[root@genni alec]# tc qdisc add dev eth0 root handle 1: prio
[root@genni alec]# tc -d qdisc show dev eth0
qdisc prio 1: bands 3 priomap 1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1
Di default la PRIO definisce una radice 1: e tre bande con priomap 1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1 che definisce la prorità delle 16 configurazioni possibili del campo TOS.
Io ho aggirato la priomap definendo due filtri:
[root@genni alec]# tc filter add dev eth0 protocol ip parent 1: handle 1 fw classid 1:1
[root@genni alec]# tc filter add dev eth0 protocol ip parent 1: handle 2 fw classid 1:2
[root@genni alec]# tc -d filter show dev eth0
filter parent 1: protocol ip pref 49151 fw
filter parent 1: protocol ip pref 49151 fw handle 0x1 classid 1:1
filter parent 1: protocol ip pref 49152 fw
filter parent 1: protocol ip pref 49152 fw handle 0x1 classid 1:1
A questo punto come detto prima associo alle due bande delle TBF per limitare il rate delle bande:
[root@genni alec]# tc qdisc add dev eth0 parent 1:1 handle 10: tbf rate $RATE_A burst 500kb latency 140ms peakrate 1mbit minburst 1540
[root@genni alec]# tc qdisc add dev eth0 parent 1:2 handle 20: tbf rate $RATE_B burst 500kb latency 140ms peakrate 1mbit minburst 1540
Il valori di rate è inespresso perche sarà quello che varieremo nel corso dei test, mentre nel corso di prove precedenti si sono definiti i valori ottimali per le altre variabili.Ponendo ad esempio RATE_A = 0.3mbit e RATE_B = 0.7mbit otteniamo:
[root@genni alec]# tc -d qdisc show dev eth0
qdisc tbf 20: rate 91750bps burst 500Kb/8 mpu 0b peakrate 1Mbit mtu 1539b/8 mpu 0b
lat 170.9ms
qdisc tbf 10: rate 39321bps burst 500Kb/8 mpu 0b peakrate 1Mbit mtu 1539b/8 mpu 0b
lat 170.9ms
qdisc prio 1: bands 3 priomap 1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1
quello che otteniamo è un'architettura del tipo:

Fig.
3.11
3.3.2La qdisc CBQ
Nella CBQ definiamo due tipi di classi associati ai due flussi di traffico che vogliamo siano gestiti. Dopo aver controllato come in precedenza l'eventuale esistenza di elementi per il controllo del traffico attivi già attivi, procediamo alla colorazione del traffico in uscita tramite iptables. L'operazione è identica al caso di qdisc PRIO quindi non ripeteremo quanto detto.
Per prima cosa allora definiamo lo scheduler CBQ:
[root@genni alec]# tc qdisc add dev eth0 root handle 10: cbq bandwidth 4Mbit avpkt 1000
La banda qui definita non è un limite ma solo un'informazione che serve allo scheduler per parametrizzare la media mobile che è alla base dello shaper CBQ.
Definiamo quindi la radice dell'albero delle classi:
[root@genni alec]# tc class add dev eth0 parent 10: classid 10:1 cbq bandwidth 4Mbit rate 1Mbit allot 1514 prio 8 maxburst 20 avpkt 1000 bounded
Particolare attenzione va posta al rate e alla specifica baunded. Il rate specificato qui e il rate totale di tutta la radice e i suoi rami e la parola baunded significa che non può in alcun modo in caso di necessità prendere in prestito banda da altre qdisc.
Definiamo ora le due clasii vere e proprie:
[root@genni alec]# tc class add dev eth0 parent 10:1 classid 10:2 cbq bandwidth 4Mbit rate $RATE_A allot 1514 prio 5 maxburst 20 avpkt 1000 bounded
[root@genni alec]# tc class add dev eth0 parent 10:1 classid 10:3 cbq bandwidth 4Mbit rate $RATE_B allot 1514 prio 5 maxburst 20 avpkt 1000
Anche qui è lasciato in forma di varianibe il rate che sarà variato nel corso dei test. E' importante notare che solo la classe 10:2 è baunded mentre la 10:3 se satura può chiedere in prestito alla 10:2 della banda se questa non è attualmente interamente utilizzata. Assoceremo il traffico marchiato con 1 proprio alla classe 10:2. In pratica vogliamo che comenque il traffico H.323 non superi un rate massimo, mentre per il traffico ordinario vogliamo siano possibili picchi fino al massimo della banda consentita dalla rete a genni, così come sarebbe ordinariamente .
Definiamo quindi i filtri:
[root@genni alec]# tc filter add dev eth0 parent 10:1 protocol ip prio 10 handle 1 fw
flowid 10:2
[root@genni alec]# tc filter add dev eth0 parent 10:1 protocol ip prio 10 handle 2 fw
flowid 10:3
Con questi comandi le impostazioni risultano:
qdisc:
qdisc cbq 10: rate 4Mbit cell 8b (bounded,isolated) prio no-transmit/8 weight 4Mbit allot 1514b
level 2 ewma 5 avpkt 1000b maxidle 58us
classi:
class cbq 10: root rate 4Mbit cell 8b (bounded,isolated) prio no-transmit/8 weight 4Mbit allot 1514b
level 2 ewma 5 avpkt 1000b maxidle 58us
class cbq 10:1 parent 10: rate 1Mbit cell 8b (bounded) prio no-transmit/8 allot 1514b
level 1 ewma 10 avpkt 1000b maxidle 112us
class cbq 10:2 parent 10:1 rate 19660bps cell 8b (bounded) prio 5/5 weight 19660bps allot 1514b
level 0 ewma 5 avpkt 1000b maxidle 43422us
class cbq 10:3 parent 10:1 rate 1Mbit cell 8b prio 5/5 weight 1Mbit allot 1514b
level 0 ewma 5 avpkt 1000b maxidle 5074us
filtri:
filter parent 10: protocol ip pref 10 fw
filter parent 10: protocol ip pref 10 fw handle 0x1 classid 10:2
filter parent 10: protocol ip pref 10 fw handle 0x2 classid 10:3
firewall:
Chain PREROUTING (policy ACCEPT)
target prot opt source destination
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
MARK all -- genni.ing.uniroma1.it anywhere MARK set 0x2
MARK udp -- genni.ing.uniroma1.it anywhere TOS match Minimize-Delay MARK set 0x1
l'architettura risultante è simile alla precedente:

Fig. 3.12
3.3.3 Test di funzionamento degli shaper
Controlliamo ora, prima dei test di qualità, l'effettivo funzionamento degli shaper. Per fare ciò usiamo il programma nload che controlla il traffico in ingresso e uscita dall'interfaccia di rete eth0.

Fig.
3.13
Facciamo partire ohphone sia su genni che su comel tramite il comando:
[alec@genni alec]$ ohphone -ln --videotransmit --videoreceive x11
Una volta connessi ossserviamo che facendo scorrere immagini prese da scheda TV presente su genni, questo host ha un traffico medio di uscita che si assesta oltre i 500kbit/s con una oscillazione costante da cirac 280kbit/s a 850kbit/s.
Mettiamo in funzione ora lo shaper PRIO con una limitazione del rate in uscita di 300 kbit/s.
Osserviamo che dopo 4-5 secondi di assestamento lo shaper incomincia a non pernetere rate superiori ai 300kbit/s imponendo così una media di circa 220kbit/s.
Guardiamo le statistiche :
[root@genni alec]# tc -s qdisc show dev eth0
qdisc tbf 20: rate 91750bps burst 500Kb peakrate 1Mbit minburst 1539b lat 170.9ms
Sent 153789 bytes 2302 pkts (dropped 0, overlimits 0)
qdisc tbf 10: rate 39321bps burst 500Kb peakrate 1Mbit minburst 1539b lat 170.9ms
Sent 3433524 bytes 3772 pkts (dropped 3856, overlimits 17232)
backlog 19851b 21p
qdisc prio 1: bands 3 priomap 1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1
Sent 3588292 bytes 6075 pkts (dropped 3856, overlimits 0)
backlog 21p
Eliminiamo ora la qdisc PRIO e inseriamo le istruzioni per la qdisc CBQ con la stesa limitazione sul rate massimo. Anche qui lo shaper ha un tempo di atenza anche se molto più breve intorno ai 2 secondi, quindi anche in questo caso il rate si assesta su massimi intorno ai 300kbit/s e la media sui 220. Guardiamo le statistiche:
[root@genni alec]# tc -s class show dev eth0
class cbq 10: root rate 4Mbit (bounded,isolated) prio no-transmit
Sent 7816759 bytes 8262 pkts (dropped 0, overlimits 0)
borrowed 0 overactions 0 avgidle 1562 undertime 0
class cbq 10:1 parent 10: rate 1Mbit (bounded) prio no-transmit
Sent 7814554 bytes 8254 pkts (dropped 0, overlimits 0)
borrowed 0 overactions 0 avgidle 94717 undertime 0
class cbq 10:2 parent 10:1 rate 39321bps (bounded) prio 5
Sent 7806605 bytes 8168 pkts (dropped 0, overlimits 16582)
backlog 53p
borrowed 0 overactions 6168 avgidle -5741 undertime 18809
class cbq 10:3 parent 10:1 rate 1Mbit prio 5
Sent 7949 bytes 86 pkts (dropped 0, overlimits 0)
borrowed 0 overactions 0 avgidle 133045 undertime 0
La classe 10:2 ha spedito 8168 pacchetti mentre ce ne sono stati 16582 andati oltre il limite.
Gli shaper quindi sono stati implementati correttamente , ora bisogna analizzare l'impatto della limitazione di banda sulla qualità dell'immagine trasmessa sotto l'azione delle due tecniche di controllo del traffico.
3.4.1 Test QoS
Provato l'effettivo funzionamento dei limitatori di banda, vediamo che effetto questi hanno sulla trasmissione di filmati tramite software di comunicazione H.323. Come sequenze di test si sono prese in considerazione due tipologie di trasmissione. La prima dovrebbe simulare il tipo di sequenze video generate durante una videoconferenza con l'utente che parla inquadrato in primo piano, o al massimo in mezzo busto, con uno sfondo generalmente fisso. La seconda invece che simula una normale trasmissione televisiva tipo una sit-com.
I due filmati scelti sono stati presi rispettivamente da una lezione del consorzio nettuno su Rai Sat Nettuno e dalla sit-com Friends su Rai Sat Fiction, ed hanno la durata di un minuto.
Le due sequenze hanno in origine due bit rate molto diversi. Infatti la sequenza della videodidattica, che d'ora in poi denomineremo semplicemente Nettuno, presenta un rate medio di 190Kbit/s con oscillazioni dai 100 ai 300Kbit/s, mentre la sequenza della sit-com, che d'ora in poi chiameremo Friends, presenta un bit rate ben più alto. Il valore medio si attesta sui 500Kbit/sec con oscillazioni dai 250 agli 850Kbit/s. Per capire quindi quale dei due shaper utilizzare e come settare la banda disponibile nel nostro controllore del traffico abbiamo previsto una serie di test variando via via shaper, banda e sequenza video.
schema prove
|
Rate (Kbit/s) |
Shaper |
Sequenza |
|---|---|---|
|
150 |
PRIO + TBF |
Netuno, Friends |
|
150 |
CBQ |
Netuno, Friends |
|
250 |
PRIO + TBF |
Netuno, Friends |
|
250 |
CBQ |
Netuno, Friends |
|
500 |
PRIO + TBF |
Friends |
|
500 |
CBQ |
Friends |
|
750 |
PRIO + TBF |
Friends |
|
750 |
CBQ |
Friends |
.Fig. 3.14
Per il giudizio ci siamo avvalsi della metodologia Doppio Stimolo a Scala di Degrado (DSIS - Double-Stimolus Impairment Scale) la quale prevede che ogni presentazione è costituita da una coppia di stimoli: Il primo è la sequenza di riferimento, il seondo quella di test. L' insoddisfazione dei valutatori è quantificata apponendo una croce su uno dei cinque livelli di degrado presenti nella scala:
Impercettibile ..............................[ ]
Percettibile ma non fastidioso......[ ]
Leggermente fastidioso...............[ ]
Fastidioso .....................................[ ]
Molto fastidioso............................[ ]
Ho usato questa scala perchè tenere memoria dei test all'interno del PC ho dovuto comunque usare un codifica che di per se aggiunge deterioramento sia all'originale che alle sequenza di test. Apriamo una piccola parentesi su quali mezzi si sono usati per questa memorizzazione dei test. Nell'ohphone si è sfruttata l'opzione --videoreceive ppm la quale, invece di aprire una finestra contenente le immagini ricevute , memorizza una serie di immagini campionate della sequenza ricevuta. Cosa importante da mettere in evidenza è che il periodo di campionamento non è fisso ma dipende da vari fattori come il rate della sequenza video e il refresh dei quadri dell'immagine. Ottenuti i frames si è proceduto ad una codifica degli stessi in un filmato mpeg tramite il software mpeg-2 del MPEG Software Simulation Group.
Incominciamo a parlare dei risultati delle prove.
Impostiamo lo shaper PRIO su un rate trasmissivo di 150Kbit/s ed andiamo a lanciare ohphone sia sull'host genni che su comel. Quindi iniziamo una sessione H.323. Comel non ha alcuna sorgente video quindi trasmette l'immagine di default, mentre genni trasmette cio che proviene da un videregistratore che ho sistemato in ingresso alla scheda TV.


Passiamo
la sequenza Nettuno. Le immagini non risultano molto deteriorate
sulla scala
Fig. 3.15. Frames 271 e 288 di nettuno.150.tbf
DSIS, poteremo definirlo come un peggioramento Percettibile ma non fastidioso. La percezione è visibile soprattutto da effetti sulla mano che è l'unico elemento molto monile del quadro ma l'immagine è comunque abbastanza fluida.
Le cose sono ben diverse per la sequenza Friends. In questo caso la scena è addirittura incomprensibile e risultando una somma scomposta di quadri. Qui dare un giudizio è praticamente inutile comunque nella nostra scala sarebbe decisamente un Molto fastidioso.

Fig.3.16.
Frame 156 di friends.150.tbf
Interessante è passare ora alla disciplina CBQ. Innanzi tutto il frame 156 di friends.150.tbf corrisponde al frame 33 di friends.150.cbq. Questo vuol dire che comel riceve molti meno quadri da genni. Infatti le immagini sotto disciplina CBQ arrivano a scatti ed il refresh totale del quadro avviene con una frequenza di 2-3 frame al secondo. Il numero totale di frame acquisiti dall'ohphone di comel e di soli 134 contro i 493 della TBF.
Questi pochi frame sono però senza deterioramento dell'immagine. Il deterioramento è comunque Fatidioso per la lentezza dell'aggiornamento.

Fig.3.17.
Frame 33 di friends.150.cbq
Tornando ora alla sequenza Nettuno in questo caso la lentezza dell'aggiornamento è meno fastidiosa ma comunque classificabile Leggermente fastidiosa in quanto si nota il movimento a scatti della mano e della bocca.
Impostiamo ora la limitazione dello shaper PRIO ad un rate di 250 Kbit/s. Con queste impostazioni la sequenza Nettuno non presenta alcun effetto visbile di peggioramento. Questo è coerente col suo rate caratteristico di 290Kbit/s. Altrettanto non si può dire dello shaper CBQ. L'ohphone su comel resce a catturare in un minuto poco più di 800 frames contro i 1068 catturati sotto disciplina PRIO. L'immagine continua a risultare legermente a scatti, quindi il peggioramento risulta percettibile anche se non fastidioso.
I giudizi cambiano radicalmente se si va ad analizzare la sequenza Friends. Con la disciplina

Fig.
3.18. Frame 269 di friends.250.tbf
PRIO + TBF l'immagine continua ad aggiornarsi a quadri risultando Fastidiosa mentre sotto disciplina CBQ la situazione è migliore in quanto sebbene a scatti l'evoluzione della storia è più chiara e come sempre le immagini sono senza difetti. I frames raccoti in un minuto risultano 252, quindi circa 4 al secondo, contro i circa 130 della CBQ a 150Kbit/s. La sequenza risulta comunque leggermente Fastidisa.
Il deterioramento finisce di esssere fastidioso quando impostiamo lo shaper CBQ sui 500Kbit/s . In questa situazione l'immagine comincia anche ad essere fluida. I frames raccolti sono 754 quindi si ottiene una frequenza di quadro di 12-13 fps che sono una frequenza buona per applicazioni di questo tipo. Per veder questo possiamo notare la vicinanza di tre frames successivi comparandoli con tre frames successivi realativi alla stessa scena nel caso CBQ a 250Kbit/s.



Fig.
3.19 Frames 316-317-318 di Friends.500.CBQ


Fig.
3.20. Fames 128-129 di Friends.250.CBQ
Se passiamo ora alla TBF questa sopporta spostamenti lenti sia del campo che degli attori. Il deterioramneto è ancora visibile, non mina la conprensione della scena ma è definibile ancora Fastidioso. Sebbene lo shaper sia settato sul suo rate medio il taglio dei picchi trasmissivi, che nel caso di friends raggiungevano gli 850Kbit/s, si fa sentire sensibilmente. A questi rate è netta la superiorità della CBQ.

Fig.
3.21. Frames 304 di friends.500.tbf
Impostando infine il rate trasmissivo a 750Kbit/s nel caso CBQ si ha un deterioramento impercettibile della qualità trasmissiva mentre con lo shaper PRIO + TBF compare ogni tanto ancora qualche quadratino fouri fase.
Chiudiamo dicendo che i test sulla sequenza Nettuna oltre i 250Kbit/s hanno dato ovviamente come risultato un peggioramento impercettibile della qualità del servizio.
Riassumiami i risultati dei test e i dati più significativi in una tabella .
|
Rate(Kbit/s) |
Shaper |
Nettuno |
Friends |
||
|---|---|---|---|---|---|
|
Frames |
DSIS |
Frames |
DSIS |
||
|
150 |
PRIO + TBF |
830 |
P |
493 |
M |
|
150 |
CBQ |
444 |
L |
134 |
F |
|
250 |
PRIO + TBF |
1068 |
I |
611 |
M |
|
250 |
CBQ |
893 |
P |
252 |
L |
|
500 |
PRIO + TBF |
- |
|
577 |
F |
|
500 |
CBQ |
- |
|
749 |
P |
|
750 |
PRIO + TBF |
- |
|
852 |
L |
|
750 |
CBQ |
- |
|
874 |
I |
Fig. 3.22
Considerazioni sui risultati ottenuti.
I risultati dei test hanno dato una grande diversità di risultati a seconda della tecnica di controllo del traffico adottata. A dir la verità non ci aspettavamo una così netta superiorità dello shaper CBQ. Cerchiamo quindi di dare una spiegazione dei risultati ottenuti.
Per comprendere appieno cosa sia successo bisogna approfondire il discorso legato alla codifica H.261 utilizzata nell'H.323. In questa codifica ogni frame viene diviso in Macro Blocks (MB) che codificano quadri da 16x16 pixel. Nel nostro caso l'immagine è in formato QCIF quindi 144x176 pixel, quindi ogni frame è formato da 99 MB.
Sebbene ogni MB porti con se informazioni come l'indicazione del picture start code e il temporal reference per la codifica appropriata dei quadri, sopra ai macroblocchi si è voluta creare una struttura di controllo in tempo reale delle operazioni di codifica. Questo ha portato alla aggiunta degli elementi denominati Group Of Blocks (GOB). Ogni GOB costituisce un frame della sequenza video codificata e porta con se una serie di informazioni di controllo come uno start code differente per ogni GOB, in modo tale che risulti più facile la rilevazione di eventuali errori, o un group number che metta in evidenza la perdita di un frame. Nel nostro caso utilizzando il formato video QCIF abbiamo 3 GOB ognuno formato da 33 MB.

Oltre a questo i pacchetti prima di essere rilasciati vengono accodati con disciplina FIFO all'uscita del codificatore. Con lo scopo di ottenere un rate trasmissivo il più possibile costante, lo stato di riempimento della coda influenza la determinazione della soglia di quantizzazione del codificatore. Se la coda risulta piena viene alzata questa soglia. Vengono definite quindi la low trshold e la high treshold sulla coda.

Chiudiamo la parentesi sulla codifica H.261 e ritorniamo al nostro quesito: perché i due shaper hanno avuto comportamenti così diversi?
Abbiamo visto che la codifica H.261 prevede meccanismi di controllo sia sulla codifica che sulla decodifica. Quando noi limitiamo il traffico il codificatore se ne accorge?
Per testare questo aspetto andiamo a vedere il traffico RTP scambiato tra i due host. Possiamo fare ciò avviando ohphone con opzione -tt che visualizza tutti i messaggi di controllo scambiati durante una connessione H.323.
///////////////////// INIZIA LA TRASMISSIONE DATI\\\\\\\\\\\\\\\
1:08.414 RTP Transmit statistics: packets=1990 octets=1787693 avgTime=9 maxTime=16 minTime=5
1:08.414 RTP SentSenderReport: ssrc=439861623 ntp=3230540219.1049273252 rtp=0 psent=1990 osent=1787693
1:09.414 RTP Transmit statistics: packets=2095 octets=1881131 avgTime=9 maxTime=12 minTime=5
1:09.414 RTP SentSenderReport: ssrc=439861623 ntp=3230540220.1049316192 rtp=0 psent=2095 osent=1881131
1:09.993 H323RTP Receiver written timestamp 167200
1:10.414 RTP Transmit statistics: packets=2201 octets=1977235 avgTime=9 maxTime=13 minTime=4
1:10.414 RTP SentSenderReport: ssrc=439861623 ntp=3230540221.1051493250 rtp=0 psent=2201 osent=1977235
1:11.414 RTP Transmit statistics: packets=2307 octets=2073576 avgTime=9 maxTime=12 minTime=4
1:11.414 RTP SentSenderReport: ssrc=439861623 ntp=3230540222.1050046172 rtp=0 psent=2307 osent=2073576
1:12.281 H323RTP Receiver written timestamp 183840
1:12.434 RTP Transmit statistics: packets=2412 octets=2168238 avgTime=9 maxTime=14 minTime=5
1:12.434 RTP SentSenderReport: ssrc=439861623 ntp=3230540223.1135050196 rtp=0 psent=2412 osent=2168238
1:13.290 RTP OnRxSenderReport: ssrc=2562893555 ntp=2002/5/16-14:14:12.889361 rtp=192160 psent=301 osent=39732
1:13.290 RTP Receive statistics: packets=301 octets=39732 lost=0 tooLate=76 order=0 avgTime=81 maxTime=251 minTime=0 jitter=152
1:13.290 RTP SentReceiverReport: ssrc=2562893555 fraction=0 lost=0 last_seq=44272 jitter=1217 lsr=0 dlsr=0
1:13.454 RTP Transmit statistics: packets=2517 octets=2264552 avgTime=9 maxTime=14 minTime=5
1:13.454 RTP SentSenderReport: ssrc=439861623 ntp=3230540224.1221299480 rtp=0 psent=2517 osent=2264552
Inizialmente non è inserito alcuno shaper. Sono visibili le statistiche di trasmissione dei pacchetti. Il valore interessante di cui osservare l'evoluzione è avgTime il quale indica il tempo medio di spedizione dei pacchetti da aprte della coda del codificatore. Inseriamo lo shaper CBQ.
/////////////// INIZIA AD OPERARE LA CBQ \\\\\\\\\\\\\\\\\\\\\\
1:14.321 H323RTP Receiver written timestamp 200480
1:16.385 H323RTP Receiver written timestamp 217120
1:18.564 RTP Transmit statistics: packets=2621 octets=2356677 avgTime=50 maxTime=1410 minTime=6
1:18.564 RTP SentSenderReport: ssrc=439861623 ntp=3230540229.1693356076 rtp=0 psent=2621 osent=2356677
1:23.504 RTP Transmit statistics: packets=2725 octets=2446566 avgTime=49 maxTime=1350 minTime=6
1:23.504 RTP SentSenderReport: ssrc=439861623 ntp=3230540234.1435926482 rtp=0 psent=2725 osent=2446566
1:28.574 RTP Transmit statistics: packets=2830 octets=2540072 avgTime=50 maxTime=1386 minTime=5
1:28.574 RTP SentSenderReport: ssrc=439861623 ntp=3230540239.1736270312 rtp=0 psent=2830 osent=2540072
1:31.558 H225 Received PDU: empty
1:31.558 H225 Handling PDU: Facility callRef=29648
1:31.558 H245 Received PDU: command miscellaneousCommand
1:31.558 H261Codec lost partial picture message ignored, not implemented
1:31.558 Codec OnMiscellaneousCommand: lostPartialPicture
1:31.558 H323 InternalEstablishedConnectionCheck: connectionState=EstablishedConnection fastStartState=FastStartDisabled
1:31.559 H323 InternalEstablishedConnectionCheck: connectionState=EstablishedConnection fastStartState=FastStartDisabled
Come possiamo vedre il codificatore si accorge instantaneamente delle difficoltà incontrate nell'invio dei pacchetti e reagisce rallentando il rate di spedizione dei pacchetti da 9 a 50ms. Non appena stacco la CBQ il parametro avgTime si riporta al valore di 9ms.
//////////////// STACCO LA CBQ \\\\\\\\\\\\\\\\\\\\\\\\\\\
1:31.884 RTP Transmit statistics: packets=2936 octets=2637029 avgTime=32 maxTime=1300 minTime=4
1:31.884 RTP SentSenderReport: ssrc=439861623 ntp=3230540242.3067590660 rtp=0 psent=2936 osent=2637029
1:32.884 RTP Transmit statistics: packets=3042 octets=2735415 avgTime=9 maxTime=13 minTime=5
1:32.884 RTP SentSenderReport: ssrc=439861623 ntp=3230540243.3067302962 rtp=0 psent=3042 osent=2735415
1:33.884 RTP Transmit statistics: packets=3147 octets=2829292 avgTime=9 maxTime=12 minTime=5
.........................
1:42.914 RTP Transmit statistics: packets=4116 octets=3705996 avgTime=9 maxTime=12 minTime=5
1:42.914 RTP SentSenderReport: ssrc=439861623 ntp=3230540253.3196268958 rtp=0 psent=4116 osent=3705996
Inseriamo ora lo shaper PRIO +TBF.
////////////////// ATTACCO LA PRIO + LA TBF\\\\\\\\\\\\\\\\
1:43.385 H225 Received PDU: empty
1:43.385 H225 Handling PDU: Facility callRef=29648
1:43.386 H245 Received PDU: command miscellaneousCommand
1:43.386 H261Codec lost partial picture message ignored, not implemented
1:43.386 Codec OnMiscellaneousCommand: lostPartialPicture
1:43.386 H323 InternalEstablishedConnectionCheck: connectionState=EstablishedConnection fastStartState=FastStartDisabled
1:43.386 H323 InternalEstablishedConnectionCheck: connectionState=EstablishedConnection fastStartState=FastStartDisabled
1:43.531 H225 Received PDU: empty
1:43.531 H225 Handling PDU: Facility callRef=29648
1:43.532 H245 Received PDU: command miscellaneousCommand
1:43.532 H261Codec lost partial picture message ignored, not implemented
1:43.532 Codec OnMiscellaneousCommand: lostPartialPicture
1:43.532 H323 InternalEstablishedConnectionCheck: connectionState=EstablishedConnection fastStartState=FastStartDisabled
1:43.532 H323 InternalEstablishedConnectionCheck: connectionState=EstablishedConnection fastStartState=FastStartDisabled
1:43.914 RTP Transmit statistics: packets=4222 octets=3801811 avgTime=9 maxTime=11 minTime=5
1:43.914 RTP SentSenderReport: ssrc=439861623 ntp=3230540254.3196247488 rtp=0 psent=4222 osent=3801811
1:44.111 H225 Received PDU: empty
1:44.111 H225 Handling PDU: Facility callRef=29648
1:44.111 H245 Received PDU: command miscellaneousCommand
1:44.111 H261Codec lost partial picture message ignored, not implemented
1:44.111 Codec OnMiscellaneousCommand: lostPartialPicture
1:44.111 H323 InternalEstablishedConnectionCheck: connectionState=EstablishedConnection fastStartState=FastStartDisabled
1:44.111 H323 InternalEstablishedConnectionCheck: connectionState=EstablishedConnection fastStartState=FastStartDisabled
1:44.301 H225 Received PDU: empty
1:44.301 H225 Handling PDU: Facility callRef=29648
1:44.301 H245 Received PDU: command miscellaneousCommand
1:44.301 H261Codec lost partial picture message ignored, not implemented
1:44.302 Codec OnMiscellaneousCommand: lostPartialPicture
Osserviamo come inizi un frequente scambio di messaggi ( anche 7-8 messaggi ogni millesimo di secondo!) . Questi indicano problemi di decodifica come continue perdite di parti di frame ma la codifica non reagisce in alcun modo inputando ciò a problemi di rete.
Alla luce di quanto osservato risulta chiaro come dal punto di vista del codificatore i pacchetti scartati dovuti al riempimento del secchio della TBF sono a tutti gli effetti inviati. La CBQ invece non scarta i pacchetti ma ne rinvia la trasmissione. Questo causa un riempimento della coda d'uscita del decodificatore il quale reagisce variando il rate di codifica.