tc
pentru aplicarea strategiilor de QoStc
tc
ce simplifică aplicarea politicilor de QoS (HTB-Tools
, tcng
)
În cadrul acestui laborator vom folosi următoarea topologie formată din trei mașini virtuale (gateway
, client1
și client2
). Acestea sunt conectate conform schemei de mai jos:
Pentru a pregăti configurația de laborator va trebui să descărcați pe mașina fizică (mjolnir
), în directorul saisp/
, arhiva laboratorului:
student@mjolnir:~/saisp$ wget --user=user-curs --ask-password http://repository.grid.pub.ro/cs/saisp/laboratoare/lab-11.zip student@mjolnir:~/saisp$ unzip lab-11.zip
În urma dezarhivării rezultă trei fișiere imagine KVM (format qcow2
, gateway.qcow2
, client1.qcow2
și client2.qcow2
) și un script de pornire a topologiei (lab11-start
). Imaginea de bază base.qcow2
este deja în directorul saisp/
și este baza pentru celelalte trei fișiere: gateway.qcow2
, client1.qcow2
și client2.qcow2
.
Puteți urma pașii de mai sus pentru a descărca infrastructura KVM pentru laborator pentru lucru acasă.
Pentru pornirea topologiei de mai sus, rulați scriptul lab11-start
:
student@mjolnir:~/saisp$ ./lab11-start
Pentru accesarea celor trei mașini (gateway
, client1
și client2
) folosiți SSH către adresele IP aferente fiecăreia. Pentru conectarea la mașina virtuală pentru gateway
folosiți comanda
student@mjolnir:~/saisp$ ssh -l root 192.168.1.3
pentru conectarea la client1
folosiți comanda
student@mjolnir:~/saisp$ ssh -l root 192.168.1.1
iar pentru conectarea la client2
folosiți comanda
student@mjolnir:~/saisp$ ssh -l root 192.168.1.2
Parola pentru cele trei mașini virtuale este student
atât pentru utilizatorul root
cât și pentru utilizatorul student
.
sudo iptables -t nat -A POSTROUTING -o eno1 -j MASQUERADE
Vă invităm să evaluați activitatea echipei de SAISP și să precizați punctele tari și punctele slabe și sugestiile voastre de îmbunătățire a materiei. Feedback-ul vostru este foarte important pentru noi să creștem calitatea materiei în anii următori și să îmbunătățim materiile pe care le veți face în continuare.
Găsiți formularul de feedback în partea dreaptă a paginii principale de SAISP de pe cs.curs.pub.ro într-un frame numit “FEEDBACK”.
Vă mulțumim!
Pentru a fi capabili să evaluăm politicile de QoS setate în rețeaua administrată de noi, avem nevoie de un utilitar care să genereze diferite tipuri de pachete, de diferite dimensiuni (UDP, TCP) și să măsoare viteza cu care acestea au fost transmise. Cel mai folosit utilitar pentru acest lucru este iperf
. Acesta creează pachete direct în memorie și le trimite pe rețea, eliminând overhead-ul altor dispozitive I/O (exemplu: dacă testam cu un transfer FTP se adăuga overhead-ul citirii/scrierii pe disc a fișierului transferat).
Utilitarul iperf poate rula în 2 moduri:
În cadrul acestui laborator dorim să limităm traficul de download
, astfel clientul iperf
va rula pe gateway
, iar server-ul iperf
, cel care primește traficul, va rula pe mașinile virtuale client1
și client2
.
Instalați iperf
pe toate cele 3 stații:
root@client1:~# apt-get update root@client1:~# apt-get install iperf root@client2:~# apt-get update root@client2:~# apt-get install iperf root@gateway:~# apt-get update root@gateway:~# apt-get install iperf
Pe stația client1
, porniți iperf
în modul server:
iperf -s
Pe stația gateway
, porniti iperf
în modul client:
iperf -c 192.168.1.1
După 10 secunde, atât server-ul (client1
) cât și clientul (gateway
) vor afișa statistici legate de traficul schimbat:
root@gateway:~# iperf -c 192.168.1.1 ------------------------------------------------------------ Client connecting to 192.168.1.1, TCP port 5001 TCP window size: 22.9 KByte (default) ------------------------------------------------------------ [ 3] local 192.168.1.3 port 57685 connected with 192.168.1.1 port 5001 [ ID] Interval Transfer Bandwidth [ 3] 0.0-10.0 sec 190 MBytes 159 Mbits/sec root@client1:~# iperf -s ------------------------------------------------------------ Server listening on TCP port 5001 TCP window size: 85.3 KByte (default) ------------------------------------------------------------ [ 4] local 192.168.1.1 port 5001 connected with 192.168.1.3 port 57685 [ ID] Interval Transfer Bandwidth [ 4] 0.0-10.0 sec 190 MBytes 159 Mbits/sec
Generați trafic de tip UDP
între stația gateway
și stația client2
în care lungimea pachetelor să fie de 256 de octeți (Hint: --len
).
În cadrul laboratorului, vom explora principalele strategii de QoS din Linux. Pentru a observa comportamentul strategiilor implementate, vom folosi un model de trafic, compus din:
Traficul nu va fi real ci îl vom simula cu ajutorul lui iperf
prezentat anterior.
Fiecare tip de trafic din cele considerate mai sus are anumite caracteristici și constrângeri. Pentru o funcționare optimă, trebuie să ținem seama de acestea:
Dupa cum ați observat la exercițiul anterior, o conexiune iperf
client-server generează un singur tip de trafic (implicit, trafic TCP pe portul 5001). Noi dorim să generăm mai multe tipuri de trafic, concomitent, pentru a vedea cum se afectează între ele. Deci va trebui să instanțiem mai multe astfel de perechi, în background.
Vom crea un script cu numele iperf-client1.sh
care va porni 4 servere iperf
pe stația client1
(urmăriți comentariile):
#!/bin/bash # Asculta trafic UDP, pe port-ul 8000 - fluxul de voce iperf --server --udp --port 8000 &> out1.txt & # Asculta trafic UDP, pe port-ul 6000 - fluxul video iperf --server --udp --port 6000 &> out2.txt & # Asculta trafic TCP, pe port-ul 21 - fluxul FTP # Daca nu se specifica --udp, implicit este TCP iperf --server --port 21 &> out3.txt & # Asculta trafic TCP, pe port-ul 80 - fluxul HTTP # Daca nu se specifica --udp, implicit este TCP. iperf --server --port 80 &> out4.txt & echo "iperf servers started. Now run the script on the gateway."
Vom crea pe stația gateway
un script cu numele iperf-gateway.sh
care se va conecta la cele 4 servere pornite pe stația client1
, generând cele 4 tipuri de trafic prezentate anterior. Simulăm tipurile de trafic prin generare de pachete ce au caracteristici asemănătoare cu cele din cazurile reale. Urmăriți comentariile din fișier:
#!/bin/bash IP_VM="192.168.1.1" TIME=60 # Durata unui test # Initiaza un flux UDP catre server, pe portul 8000 # Fiecare datagrama are dimensiunea de 128 octeti (tipic pentru pachetele de voce) # Se trimite la o rata de 640Kbps (dorim sa simulam 10 conversatii VoIP, a cate 64Kbps) iperf -x SC --client $IP_VM --port 8000 --udp --len 128 --bandwidth 640K --time $TIME > out3.txt 2> /dev/null & # Initiaza un flux UDP catre server, pe portul 6000 # Fiecare datagrama are dimensiunea maxima (pentru ca nu o specificam explicit) # Se trimite la o rata de 30Mbps iperf -x SC --client $IP_VM --port 6000 --udp --bandwidth 30M --time $TIME > out4.txt 2> /dev/null & # Initiaza un flux TCP catre server, pe portul 80 (HTTP) # Limitam dimensiunea unui segment la 512 octeti iperf -x SC --client $IP_VM --port 80 --mss 512 --time $TIME > out1.txt 2> /dev/null & # Initiaza un flux TCP catre server, pe portul 21 (FTP) # Dimensiunea unui segment va fi de 1400 octeti (dorim ca fluxul FTP sa fie mai agresiv) # Dimensiunea ferestrei TCP va fi de 256K (dorim ca fluxul FTP sa fie mai agresiv) iperf -x SC --client $IP_VM --port 21 --window 256K --mss 1400 --time $TIME > out2.txt 2> /dev/null & wait for i in out*; do echo; cat $i; done rm out*.txt
Rulați script-urile create anterior (iperf-client1.sh
pe stația client1
și iperf-gateway.sh
pe stația gateway
). Așteptați 60 de secunde și inspectați output-ul de pe stația gateway
. Ce observați?
Observăm că fluxurile UDP au suferit packet loss, ele neavând nici un mecanism pentru retransmitere sau reglare a vitezei în funcție e starea legăturii. Pentru fluxul video, o pierdere de pachete de câteva procente este inacceptabilă. În continuare vom studia mecanismele implicite de QoS din Linux și cum putem preveni aceste pierderi de pachete.
În Linux, strategiile de QoS se inspectează și configurează folosind comanda tc
. Termenul folosit pentru strategiile de QoS este qdisc
(de la queueing discipline
).
Afisați detalii despre qdisc-ul implicit, asociat interfeței eth0
de pe stația gateway
:
# tc qdisc show dev eth0 qdisc pfifo_fast 0: root refcnt 2 bands 3 priomap 1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1
Observați că qdisc-ul implicit este pfifo_fast
(strategia implicită de QoS):
priority FIFO
.0
conține pachete, cozile 1 si 2 NU vor fi servite.
Ce reprezintă priomap 1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1
? În Linux, kernel-ul asociază fiecărui pachet o prioritate, în functie de campul TOS (Type of Service) din header-ul IP. Prioritatea ne selectează una din cele 3 (sub)cozi din pfifo_fast: coada 0, 1 sau 2. Asocierile se fac după următorul tabel:
TOS Means Linux Priority Band ------------------------------------------------------- 0x0 Normal-Service 0 Best Effort 1 0x2 Minimize-Monetary Cost 1 Filler 2 0x4 Maximize-Reliability 0 Best Effort 1 0x6 mmc+mr 0 Best Effort 1 0x8 Maximize-Throughput 2 Bulk 2 0xa mmc+mt 2 Bulk 2 0xc mr+mt 2 Bulk 2 0xe mmc+mr+mt 2 Bulk 2 0x10 Minimize-Delay 6 Interactive 0 0x12 mmc+md 6 Interactive 0 0x14 mr+md 6 Interactive 0 0x16 mmc+mr+md 6 Interactive 0 0x18 mt+md 4 Int. Bulk 1 0x1a mmc+mt+md 4 Int. Bulk 1 0x1c mr+mt+md 4 Int. Bulk 1 0x1e mmc+mr+mt+md 4 Int. Bulk 1
Observăm că pachetele normale (cu TOS 0x00) sunt introduse în (sub)coada 1. La punctul anterior, fluxul UDP avea o pierdere semnificativă de pachete. O primă soluție ar fi marcarea pachetelor de voce și video cu un ToS favorabil (de exemplu, 0x10
), pentru a avea prioritate mai mare față de pachetele. Marcarea se realizează folosind lanțul mangle
al comenzii iptables
:
root@gateway:~# iptables -t mangle -A OUTPUT -p udp --dport 6000:8000 -j TOS --set-tos Minimize-Delay
Comanda anterioară marchează toate pachetele UDP cu porturile în intervalul 6000 și 8000 cu tag-ul Minimize-Delay
. Se observă că se poate folosi numele tag-ului în loc de valoare (0x10
).
Rulați din nou scriptul iperf-gateway.sh
pe stația gateway
și observați că procentele pierdute s-au diminuat foarte puțin. Nu e suficientă doar o prioritizare a pachetelor.
Am observat faptul că policing-ul pe baza ToS-ului nu este suficient pentru a asigura echitatea între servicii. Soluția ce se apoate aplica este dată de limitarea serviciilor FTP și HTTP.
tbf
este cea mai simplă metodă de a face traffic shaping în Linux. Este, de asemenea, un qdisc classless (ca și pfifo_fast
).
Dorim să limităm viteza pentru fluxurile FTP și HTTP (însumate) la aproximativ 9Mbps. Deoarece fluxul de voce are 640kbps, iar cel video are 30Mbps, rezulta ca o valoare acceptabila pentru viteza totală ar fi 40Mbps. Pe stația gateway
ne dorim următoarele setări:
Vom seta qdisc-ul tbf
, pe interfața eth0
astfel:
root@gateway:~# tc qdisc add dev eth0 root tbf rate 50mbit burst 128kbit latency 50ms
Rulați din nou script-ul iperf-gateway.sh
de pe stația gateway
. Asteptați 60 de secunde și inspectați output-ul afișat. Ce observați? Pierderile de pachete pentru conexiunea UDP au dispărut, qdisc-ul tbf
asigurând echitate între conexiuni.
Cu toate că suma vitezelor fluxurilor este de aproximativ 50Mbps, ea nu este distribuită așa cum ne-am fi asteptat. Fluxurile FTP și HTTP ocupa mai mult decât 9Mbps. Motivul este că limitarea a fost facută printr-o metoda classless, ce limitează întreg traficul de pe o interfață, fără a avea posibilitatea de a selecta și clasifica diferite tipuri de trafic. Soluția este dată de aplicarea unui qdisc classful, ce trateaza in mod separat diferitele clase de trafic.
htb
este un qdisc classful (tratează traficul în mod diferențiat, în funcție de clasa din care face parte). Este varianta classful a tbf
(împarte traficul pe clase, apoi aplica tbf
pe fiecare în parte).
Clasele sunt organizate într-o structura de arbore. Cu cât o clasa este mai jos în ierarhie, cu atât este mai specifică. Fiecare clasă are asociată o strategie de QoS (qdisc). În mod implicit, qdisc-ul este pfifo_fast, iar acesta poate fi modificat.
Exemplu de ierarhie:
1: root qdisc (always present) | 1:1 child class (with default qdisc) / \ / \ 1:3 1:4 leaf classes (with user-defined qdiscs) | | 30: 40: qdiscs (sfq) (sfq)
Observații:
major:
este echivalentă cu major:0
.1:1
are un qdisc asociat (cel implicit).1:3
și 1:4
le-a fost schimbat qdisc-ul implicit (în loc de pfifo_fast
, avem sfq
, un qdisc ce funcționează după modelul Round Robin)Tratarea pachetelor:
1:1
, dar NU se potrivește cu clasa 1:3
sau 1:4
, va fi tratat conform qdisc-ului implicit al clasei 1:1
, adica pfifo_fast
.1:1
ȘI cu clasa 1:3
, va fi tratat conform qdisc-ului clasei 1:3
, adica sfq
.1:4
.Clasificarea pachetelor:
match
și clasa în care trebuie inclus pachetul.Ne propunem sa alocam cate o clasa fiecarui tip de trafic din cele definite în scenariu. Fiecare clasă de trafic va fi limitată la o lățime de bandă bine definită, astfel:
Toate configurațiile vor fi făcute pe interfața eth0
a stației gateway
(politifice de traffic shaping pot fi aplicate doar pentru traficul care iese pe o interfață). Mai întâi, trebuie să ștergem orice alt qdisc de pe interfața eth0
:
root@gateway:~# tc qdisc del dev eth0 root
Adăugam qdisc-ul htb
, cu majorul 1:
(sau 1:0
):
root@gateway:~# tc qdisc add dev eth0 root handle 1: htb
Definim prima clasa, cea pentru traficul de voce. Trebuie să specificăm:
1:
1:1
(majorul trebuie sa fie același cu al părintelui, minorul poate fi orice)root@gateway:~# tc class add dev eth0 parent 1: classid 1:1 htb rate 1mbit burst 128k
Procedăm analog și pentru restul claselor:
tc class add dev eth0 parent 1: classid 1:2 htb rate 40mbit burst 128k tc class add dev eth0 parent 1: classid 1:3 htb rate 5mbit burst 128k tc class add dev eth0 parent 1: classid 1:4 htb rate 3mbit burst 128k
Verificăm qdisc-ul asociat interfeței eth0
și ierarhia de clase adăugată:
root@gateway:~# tc qdisc show dev eth0 qdisc htb 1: root refcnt 2 r2q 10 default 0 direct_packets_stat 82 direct_qlen 1000 root@gateway:~# tc class show dev eth0 class htb 1:1 root prio 0 rate 1000Kbit ceil 1000Kbit burst 128Kb cburst 1600b class htb 1:2 root prio 0 rate 40000Kbit ceil 40000Kbit burst 128Kb cburst 1600b class htb 1:3 root prio 0 rate 5000Kbit ceil 5000Kbit burst 128Kb cburst 1600b class htb 1:4 root prio 0 rate 3000Kbit ceil 3000Kbit burst 128Kb cburst 1599b
Cu toate că am definit ierarhia de clase, nu am definit cum selectăm traficul pentru aceste clase. Vom folosi noțiunea de filtre oferită de utilitarul tc
. Definim primul filtru, ce selectează trafic cu portul destinație 8000
. Trebuie să specificam:
1:
- vom atasa toate filtrele în nodul rădăcina1
- toate filtrele vor avea aceeași prioritateu32
, ce poate face match pe header-ul IP1:1
:root@gateway:~# tc filter add dev eth0 protocol ip parent 1: prio 1 u32 match ip dport 8000 0xffff flowid 1:1
Procedăm analog și cu celelalte filtre:
root@gateway:~# tc filter add dev eth0 protocol ip parent 1: prio 1 u32 match ip dport 6000 0xffff flowid 1:2 root@gateway:~# tc filter add dev eth0 protocol ip parent 1: prio 1 u32 match ip dport 21 0xffff flowid 1:3 root@gateway:~# tc filter add dev eth0 protocol ip parent 1: prio 1 u32 match ip dport 80 0xffff flowid 1:4
Verificăm filtrele create:
root@gateway:~# tc filter show dev eth0 filter parent 1: protocol ip pref 1 u32 filter parent 1: protocol ip pref 1 u32 fh 800: ht divisor 1 filter parent 1: protocol ip pref 1 u32 fh 800::800 order 2048 key ht 800 bkt 0 flowid 1:1 match 00001f40/0000ffff at 20 filter parent 1: protocol ip pref 1 u32 fh 800::801 order 2049 key ht 800 bkt 0 flowid 1:2 match 00001770/0000ffff at 20 filter parent 1: protocol ip pref 1 u32 fh 800::802 order 2050 key ht 800 bkt 0 flowid 1:3 match 00000015/0000ffff at 20 filter parent 1: protocol ip pref 1 u32 fh 800::803 order 2051 key ht 800 bkt 0 flowid 1:4 match 00000050/0000ffff at 20
Pe stația gateway, rulați din nou script-ul iperf-gateway.sh
. Asteptați 60 de secunde și inspectați output-ul afișat. Ce observați?
tbf
) pentru flow-urile UDP.Ștergeți qdisc-ul root adăugat anterior.
Realizați configurațiile necesare astfel încât să limitați stația client1
la 5 mbps, iar stația client2
la 3mbps. În plus dorim ca cei 3mbps ai stației client2
să fie împărțiți după cum urmează:
match
ale clasificatorului u32
)
Testați folosind iperf
.
Ștergeți qdisc-ul root adăugat anterior.
Creați un script ce adaugă o limită de 1mbps pentru toate adresele IP din clasa 10.0.0.0/21 (trebuie să creați câte o clasă și câte un filtru pentru fiecare adresă IP). La finalul scriptului adăugați limita de 1mbps și pentru adresele IP asociate stațiilor client1
și client2
.
Realizați un transfer între stația client2
și stația gateway
folosind iperf
. Utilizați 5 thread-uri pe partea de client a iperf
(Hint: -P
). În timpul transferului observați încărcarea stației gateway
(în mod special thread-urile kernel ksoftirq
). Acest lucru se datorează faptului că fiecare pachet trebuie să parcurgă toate filtrele adăugate secvențial. Clasificatoul u32
a venit cu o soluție pentru acest lucru: folosirea unor tabele de hash, stocând filtrele sub forma unui arbore. Cheia hash-ului este dată de un octet al adresei IP destinație (mai multe detalii în curs și la exercițiul de la BONUS).
Generarea de mână a filtrelor folosind hash tables
este anevoioasă și NU se recomandă. Vom folosi un program C, disponibil |aici.
Descărcați și compilați programul prefixtree
:
root@gateway:~# wget http://vcalinus.gemenii.ro/prefixtree.c root@gateway:~# make prefixtree cc prefixtree.c -o prefixtree root@gateway:~# ./prefixtree IPv4 u32 hash filter generator - (C) 2006 Calin Velea Syntax: prefixtree {prefix.in} {u32filters.out} {interface} {src/dst} [batch]
Vom scrie fișierul prefix.in
care trebuie să conțină pe câte o linie adresa IP pentru care vrem să ne genereze filtrele și classid
-ul asociat.
Modificați scriptul de la exercițiul 07. [10p] HTB - observarea consumului de resurse astfel încât să introducă perechea adresaIP classid
în fișierul prefix.in
în loc să adauge filtrul asociat. Adăugați și masca adresei IP (/32
).
Rulăm comanda prefixtree
pe fișierul generat:
root@gateway:~# ./prefixtree prefix.in filters.out eth0 dst lines parsed: 4066 total hashtables: 4
Observați conținutul fișierului filters.out
. Aplicăm filtrele generate:
root@gateway:~# chmod +x filters.out root@gateway:~# ./filters.out
Testați din nou folosind iperf
. Obervați faptul că stația gateway
, cea care face limitările, nu mai intră în load (thread-urile kernel ksoftirqd
au CPU usage foarte mic).
Utilitarul tc
oferă un control foarte bun asupra parametrilor QoS. Dar, din păcate, sintaxa este foarte complexă, greu de reținut și puțin lizibilă. tcng
este un utilitar cu o sintaxă mult mai expresivă, asemănătoare limbajului C. Folosind aceasta sintaxă, el poate genera comenzile tc
echivalente.
Pe stația gateway
descărcați și instalați utilitarul tcng
:
root@gateway:~# wget http://archive.debian.org/debian/pool/main/t/tcng/tcng_10b-3_amd64.deb root@gateway:~# dpkg -i tcng_10b-3_amd64.deb
Un fișier tcng
are următoarea sintaxă simplificată:
dev INTERFATA { QDISC () { class ( ACTIUNE ) FILTRU ; class ( ACTIUNE ) FILTRU ; ... class ( ACTIUNE ) FILTRU ; } }
Ne propunem să creăm un fișier tcng
, echivalent configurațiilor HTB de la 05. [10p] Traffic shaping classful:
eth0_htb.tc
.eth0
în câmpul dev
.QDISC
-ul htb
.rate xMbps
, unde x
este lățimea de bandă corespunzătoare.udp_port == ABCD
, respectiv tcp_port == ABCD
, unde ABCD
este portul corespunzător.dev eth0 { htb () { class ( rate 1Mbps ) if udp_dport == 8000 ; class ( rate 40Mbps ) if udp_dport == 6000 ; class ( rate 5Mbps ) if tcp_dport == 21 ; class ( rate 3Mbps ) if tcp_dport == 80 ; } }
Rulați tcng
pentru a obține comenzile echivalente tc
:
tcng eth0_htb.tc
Inspectați comenzile tc
generate și observați asemănările cu comenzile introduse manual.
Pe lângă controlul traficului, utilitarul tc
ne furnizează și statistici despre traficul transferat de fiecare clasă:
root@gateway:~# tc -s class show dev eth0
Scrieți un script care extrage numărul de octeți trimiși pentru una din clase. Utilizați acest script să generați un grafic folosind informațiile prezentate în 05. [20p] Baze de date Round Robin (RRD).