QoS si HTB in Linux

De la Wiki.lug.ro
Salt la: navigare, căutare

QoS si HTB in Linux

de Andrei Dumitrescu / 28.09.2005

mici corectii si adaugiri lonely wolf / 11.01.2006


În articolul de faţă îmi propun să detaliez implementarea unei soluţii de QoS folosind Linux şi HTB.

Această soluţie este ideală reţelelor care împart o singura ieşire pe internet (internet prin cablu, DSL, ISDN, etc) folosind NAT (Network Address Translation). Atât în România cât şi în străinătate acest tip de configurare (stub network) este extrem de răspândit pentru reţelele mici şi medii (maxim câteva sute de calculatoare).

Subiectul de fata este considerat avansat, de aceea o bază solidă în ceea ce priveşte Linux Networking şi QoS este necesară pentru înţelegerea în profunzime. Anumite expresii le voi lăsa în limba engleza intenţionat, deoarece traducerea lor ar fi nenaturală şi nejustificată.

Scopul procesului de "bandwidth management" este de a nu permite unui singur protocol sau client să consume toata banda existentă sau mai mult de cât îi este permis. Banda acesta trebuie să fie împărţită între toate protocoalele în funcţie de necesităţi. Noi trebuie să fim cei care controlează coada şi nu ISP-ul sau modemul de dsl/catv unde nu avem nici o influenţa. Aceasta trebuie să fie minima sau cel mai bine să nu existe. Exista şi un trade-off. In general se poate folosi maxim 75% din banda oferită de ISP. Pentru a avea coada mică suntem obligaţi să transmitem la doar 70-75% din capacitate. Oricum informaţia utilă nu scade considerabil dacă transmitem la 75% fiindcă în momentul în care transmitem la 100%, multe pachete se pierd, apar coliziuni etc şi trebuie retransmise.

Foarte important este de reţinut că avem control numai asupra traficului de tip egress, adică cel pe care îl generăm noi. Acest trafic poate fi prioritizat, împărţit intre clienţi, intre protocoale etc. Acest lucru se numeşte "traffic shaping". Nu avem control asupra traficului de tip ingress ("traffic policing").

Pentru început îmi propun să explic câţiva termeni utilităţi şi câteva concepte:

  • Trafic interactiv = trafic care trebuie să aibă delay minim şi care în general este format din doar unul sau câteva pachete. Nu ne interesează volumul de trafic (throughput-ul) ci doar delay-ul, timpul necesar de a ajunge de la sursă la destinaţie. În această categorie se încadrează: ssh, VoIP - Voice Over IP, pachetele ACK, ACK/SYN, FIN, RST, FTP-Control (port 21), telnet, etc
  • Bulk traffic = trafic care nu are nevoie de delay scăzut, doar volumul contează. Ex: email, P2P, web download, ftp-data, bittorent etc
  • Coada (queue) şi disciplina cozii (queueing discipline). În Linux (dar şi pe alte routere dedicate (ex. Cisco)), traficul care trebuie trimis către o destinaţie este salvat într-un buffer, într-o coadă, asta fiindcă în general computerele şi reţeaua din spatele routerului trimit date mult mai repede decât router-ul sau conexiunea acestuia la Internet pot procesa. Pentru a nu pierde pachetele, router-ul le pune într-o coadă. Notă: Când spun router ma refer atât la un router dedicat Cisco dar şi la un calculator care rulează Linux şi are funcţie de router.

Această coadă este de fapt un buffer în care pachetele aşteaptă să fie livrate. Dimensiunea cozii este de maxim câţiva MB. O coadă de 3MB este considerată gigantică. Cu fiecare coada se asociază o disciplină (queueing discipline), adică modul/ordinea în care pachetele sunt puse în coadă şi livrate. Acestă disciplină este de obicei implicit de tip FIFO (First In First Out). Fiecare pachet aşteaptă ca toate cele dinaintea lui să fie livrate pentru a-i veni rândul. Pentru a maximiza bandwidth-ul acest lucru este foarte bun, dar se maximizează şi delay-ul. In acest mod traficul interactiv are mult de suferit.

  • qdisc = queueing discipline (disciplina cozii)
  • classfull queueing qdisc = traficul se poate împărţi în clase, există clase părinte, clase copil, clase de tip leaf (clase din care nu mai derivează nimeni). Principiul este asemănător cu cel din programarea orientată pe obiecte.
  • prio = prioritatea oferită fiecărei clase de a împrumuta de la clasa părinte. Cu cat prio este mai mica cu atât prioritatea este mai mare.
  • classless qdisc = nu exista nici o subdiviziune a traficului, totul este la grămadă
  • clasa = un tip de trafic (ex. tot traficul care se duce către yahoo.com de la un anume IP către portul 25).
  • rate = traficul minim garantat pentru fiecare clasa
  • ceil = traficul maxim pe care are voie să-l atingă o clasa
  • burst = cantitatea de trafic care se trimite la ceil-speed până se trece să se servească o alta clasă
  • cburst= cantitatea de trafic care se trimite la wire-speed până se trece să se servească o alta clasă

Dacă o clasă nu foloseşte toata banda care îi este alocată, o clasa copil poate împrumuta banda de la clasa părinte (dacă aceasta are surplus de banda).

  • quantum = numărul de bytes împrumutaţi de părinte clasei copil până să împrumute unei alte clase. Nu trebuie să fie mai mic decât MTU (1500 bytes)
  • egress = traficul expediat prin interfaţa externă a border router-ului (gateway). Aici intervenim.
  • ingress = traficul recepţionat prin interfaţa externă a border router-ului (gateway). Nu putem interveni în managementul lui.
  • Traffic Shaping = ignorarea sau întârzierea pachetelor ce trebuie expediate, în vederea respectării unei anumite rate şi astfel a minimizării cozii şi respectiv delay-ului.
  • Traffic Policing = ignorarea traficului recepţionat (efecte pozitive se obţin greu sau imposibil)
  • filter = împarte traficul în categorii de trafic în funcţie de un criteriu şi îl asociază cu o clasă (ex: traficul de tip ftp în clasa 2:20).
  • HTB = Hierarchical Token Bucket. Un algoritm classfull care foloseşte noţiunea de token (buffer) şi bucket (buffer). Este cea mai folosită şi la îndemână modalitate de QoS în Linux. Exemplul de fata va folosi HTB.
  • root qdisc = root qdisc este qdisc ataşat direct unei interfeţe.

Fiecărei interfeţe de reţea îi este ataşat un qdisc. Qdisc şi class sunt legate una de cealaltă. Qdisc este cel care trimite traficul generat de fiecare clasă. Fiecare clasă are un qdisc.

Având explicate majoritatea conceptelor din HTB, putem da un exemplu practic.

Exemplul 1

Presupunem ca exista un router Linux, care leagă o reţea locala de mai multe calculatoare la Internet. Exista un singur IP public, iar router-ul face NAT. Presupunem ca ISP oferă o legătură asimetrică de 1024 Kbs download şi 256Kbs upload. După un studiu amănunţit am identificat traficul generat ca fiind de tip: dns, web, ftp, ssh, email şi p2p. Acest tip de trafic vrem să-l prioritizăm după nevoie. Vrem ca în momentul în care dc++ sau eMule rulează, să primească doar traficul care rămâne nefolosit (dacă exista), iar dacă nici un alt protocol nu foloseşte banda vrem ca p2p să primească tot. Vrem ca web,ssh şi dns şi pachetele care conţin flagurile tcp ack, syn/ack, rst, fin să aibă delay minim, astfel încât paginile să se încarce repede chiar dacă altcineva face download la un fişier mare sau trimite mailuri.

Downlod-urile, ftp şi mailul vrem să aibă delay mare dar şi throughput mare. P2P (kazaa, dc++, eMule, bittorent) vrem să aibă prioritate minimă.

In Linux HTB se implementează folosind tc (man tc). tc însemnă traffic control. Toate comenzile le vom scrie într-un script pentru o mai uşoară înţeleger a lor. În plus scriptul va porni la bootare, imediat după interfaţa Ethernet care generează traficul. Pentru a clasifica traficul vom folosi iptables cu opţiunea --mark. Există şi alte modalităţi (ex: u32, câmpul tos din header-ul ip, layer 7 classification etc).

#!/bin/bash
#definim variabilele
######################start htb#####################################
CEIL=192 #(75% din uploadul maxim posibil)
IF=eth0 #interfata pe care pleaca pachetele

#flush everything
tc qdisc del dev $IF root
iptables -F OUTPUT -t mangle

####################definirea claselor de trafic###########################
##root qdisc este atasat interfetei. Exista unul singur şi are ip 1:0 sau 1: Default 12 presupune ca traficul care nu se incadreza in nici o clasa va intra in clasa 12. Acesta este pentru noi traficul p2p.

tc qdisc add dev $IF root handle 1: htb default 12

#class 1:1 attached to root qdisc. Fiecare clasa are un ID care o identifica unic.
#rate=banda minim garantata, ceil=manda maxima pe care o primeste (prin imprumut de la clasa parinte). Din aceasta clasa vor deriva toate celelalte clase, deci primeste tot, iar RATE=CEIL
tc class add dev $IF parent 1: classid 1:1 htb rate ${CEIL}kbit ceil ${CEIL}kbit

#class 1:10 -> 80kbit-192kbit for ACK,SSH. Prio=1, rezulta prioritate maxima de a imprumuta de la parinte.
tc class add dev $IF parent 1:1 classid 1:10 htb rate 80kbit ceil ${CEIL}kbit prio 1

#class 1:11 (email), prioritatea este mai mica deci imprumuta numai dupa ce clasa 1:10 este complet satisfacuta.
tc class add dev $IF parent 1:1 classid 1:11 htb rate 108kbit ceil ${CEIL}kbit prio 2

#default class 1:12 for bulk traffic (aMule, alti p2p). Are garantat doar 8kbit dar poate ajunge la maxim (1024 kbs) daca nu exista alt tip de traffic.
tc class add dev $IF parent 1:1 classid 1:12 htb rate 8kbit ceil ${CEIL}kbit prio 3

##############marcarea si atasarea traficului claselor#########################
#clasificam traficul cu iptables si apoi il atasam clasei 1:10
#mark ack,syn ack,rst,fin, ssh packets and add them to class 1:10 (high priority)
iptables -A OUTPUT -t mangle -o $IF -p tcp --sport 22 -j MARK --set-mark 1
iptables -A OUTPUT -t mangle -o $IF -p tcp --dport 22 -j MARK --set-mark 1

iptables -A OUTPUT -t mangle -o $IF -p tcp --tcp-flags SYN,RST,ACK SYN,FIN -j MARK --set-mark 1
#mark dns packets
iptables -A OUTPUT -t mangle -o $IF -p udp --dport 53 -j MARK --set-mark 1
iptables -A OUTPUT -t mangle -o $IF -p tcp --dport 53 -j MARK --set-mark 1

#mark web packets
iptables -A OUTPUT -t mangle -o $IF -p tcp --dport 80 -j MARK --set-mark 1
#atasam traficul clasei 1:10 (prioritate maxima)
tc filter add dev $IF parent 1: protocol ip handle 1 fw classid 1:10

#mark smtp packets
iptables -A OUTPUT -t mangle -o $IF -p tcp --dport 25 -j MARK --set-mark 2
#mark pop3 packets
iptables -A OUTPUT -t mangle -o $IF -p tcp --dport 110 -j MARK --set-mark 2
#mark imap packets
iptables -A OUTPUT -t mangle -o $IF -p tcp --dport 143 -j MARK --set-mark 2

#atasam traficul clasei 1:11 (prioritatea medie)
tc filter add dev $IF parent 1: protocol ip handle 2 fw classid 1:11

#restul traficului intra in clasa default 1:12. Nu este nevoie sa-l clasificam.
#Nota: pentru eMule si alti p2p, clasificare traficului este greoaie sau imposila fiindca emule foloseste sute de conexiuni cu clienti diferiti folosind porturi aleatoare. Pentru a-l clasifica corect trebuie să folosim o extensie a lui iptables care clasifica in functie de layer7. (kernelul si iptables trebuie recompilate pentru a suporta acesta extensite optionala - http://l7-filter.sourceforge.net/L7-HOWTO-Netfilter )

#celor 3 clase le oferim sanse egale folosind sfq (Stochastic Fair Queuing). Este optional
tc qdisc add dev $IF parent 1:10 handle 100: sfq perturb 10
tc qdisc add dev $IF parent 1:11 handle 110: sfq perturb 10
tc qdisc add dev $IF parent 1:12 handle 120: sfq perturb 10

#pentru a avea delay minim, facem drop la 10% din banda oferita de ISP pe ingress
##INGRESS - drop everithing over 948kbit
tc qdisc del dev $IF ingress
tc qdisc add dev $IF handle ffff: ingress
tc filter add dev $IF parent ffff: protocol ip prio 10 u32 match \
ip src 0.0.0.0/0 police rate 948kbit burst 80kbit drop flowid :1



Acest script poate fi folosit şi adaptat pentru orice reţea mică-medie care foloseşte o singura conexiune la Internet. Bineînţeles ca traficul se prioritizează în funcţie de preferinţele fiecăruia.

Pentru a testa că funcţioneză, recomand folosirea lui iftop (http://www.ex-parrot.com/~pdw/iftop/) în timp ce se generează trafic. Pentru a fi clar este bine ca pe o legătură super rapidă între două calculatoare (fast Ethernet 100Mbs) să se simuleze o legătura extrem de înceată ca să se observe diferenţa. Se setează HTB şi se limitează câteva protocoale la câţiva zeci de Kbytes şi se verifică în timp real cu iftop că funcţionează corect.

Ex: limitam ftp la 10 kbit, web la 2kbit şi transferul de fişiere cu smb (Server Message Block) la 15 kbit. Apoi se încearcă transferul de fişiere mari cu ftp sau smb sau interogarea serverului web.

Exemplu 2

Presupunem ca funcţionăm ca un mic ISP de cartier şi că vrem să împărţim banda intre 3 clienţi în funcţie de tipul de abonament. Dacă exista banda nefolosită de un client, poate fi oferită unui alt client în ordinea importantei: client2, client1, client3

Total = 2048 kbit
Client 1 (ip 10.0.0.1) = 128 kbit
Client 2 (ip 10.0.0.2) = 256 kbit
Client 3 (ip 10.0.0.3) = 1795 kbit

#Cream o clasa root din care deriva toate celelalte trei clase si ataşăm o prioritate fiecăreia
RATE1=128
RATE2=256
RATE3=1795
CEIL=2048
IF=ppp0

tc qdisc del dev $IF root
iptables -F OUTPUT -t mangle
##root qdisc
tc qdisc add dev $IF root handle 1: htb

#class 1:10 attached to root qdisc (din ea deriva cele 3 clase)
tc class add dev $IF parent 1: classid 1:10 htb rate ${CEIL}kbit ceil ${CEIL}kbit

#class 1:20 petru client1
tc class add dev $IF parent 1:10 classid 1:20 htb rate ${RATE1}kbit ceil ${CEIL}kbit prio 1

#class 1:30 pentru client2
tc class add dev $IF parent 1:10 classid 1:30 htb rate ${RATE2}kbit ceil ${CEIL}kbit prio 0

#class 1:40 pentru client3
tc class add dev $IF parent 1:10 classid 1:40 htb rate ${RATE3}kbit ceil ${CEIL}kbit prio 2

#clasificam traficul cu iptables si il atasam fiecarei clase
iptables -A OUTPUT -t mangle -o $IF -d 10.0.0.1 -j MARK --set-mark 20
iptables -A OUTPUT -t mangle -o $IF -d 10.0.0.2 -j MARK --set-mark 30
iptables -A OUTPUT -t mangle -o $IF -d 10.0.0.3 -j MARK --set-mark 40

tc filter add dev $IF parent 1: protocol ip handle 20 fw classid 1:20
tc filter add dev $IF parent 1: protocol ip handle 30 fw classid 1:30
tc filter add dev $IF parent 1: protocol ip handle 40 fw classid 1:40

#optional, oferim sanse egale fiecarei grupe
tc qdisc add dev $IF parent 1:20 handle 100: sfq perturb 10
tc qdisc add dev $IF parent 1:30 handle 110: sfq perturb 10
tc qdisc add dev $IF parent 1:40 handle 120: sfq perturb 10

Informaţiile prezentate mai sus vă pot oferi un bun start în învăţarea despre QoS şi HTB folosind Linux. În nici un caz nu sunt complete sau suficiente. Pot spune ca "just scratched the surface". Pentru mai multe informaţii citiţi resursele.

Resurse

man page a lui tc

http://linux-net.osdl.org/index.php/Iproute2

Rusty's Remarkably Unreliable Guides

man page pt iptables

Linux Advanced Routing & Traffic Control

http://www.docum.org/docum.org/docs/

HTB Linux queuing discipline manual - user guide

A Traffic Control Journey: Real World Scenarios

ADSL Bandwidth Management HOWTO

http://forums.gentoo.org/viewtopic-t-347779-highlight-htb.html

http://gentoo-wiki.com/HOWTO_Packet_Shaping

http://forums.gentoo.org/viewtopic-t-225863-highlight-htb.html

l7-filter

ipp2p

Fair NAT for Linux Routers