QoS si HTB in Linux

De la Wiki.lug.ro
Versiunea din 10 ianuarie 2006 19:38, autor: 82.76.1.145 (Discuție) (diverse corectii, explicat/editat linkuri catre resurse)

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

In articolul de fata imi propun sa detaliez implementarea unei solutii de QoS folosind Linux si HTB.

Aceasta solutie este ideala retelelor care impart o singura iesire in internet (internet prin cablu, DSL, ISDN, etc) folosind NAT (Network Address Translation). Atat in Romania cat si in strainatate acest tip de configurare (stub network) este extrem de raspandit pentru retelele mici si medii (maxim cateva sute de calculatoare).

Subiectul de fata este considerat avansat, de aceea o baza solida in ceea ce priveste Linux, Networking si QoS este necesara pentru intelegerea in profunzime. Anumite expresii le voi lasa in limba engleza intentionat, fiindca traducera lor ar fi nenaturala si nejustificata.

Scopul procesului de "bandwidth management" este de a nu permite unui singur protocol sau client sa consume toata banda existenta sau mai mult de cat ii este permis. Banda acesta trebuie sa fie impartita intre toate protocoalele in functie de necesitati. Noi trebuie sa fim cei care controleza coada si nu ISP-ul sau modemul de dsl/catv unde nu avem nici o influenta. Aceasta trebuie sa fie minima sau cel mai bine sa nu existe. Exista si un trade-off. In general se poate folosi maxim 75% din banda oferita de ISP. Pentru a avea coada mica suntem obligati sa transmitem la doar 70-75% din capacitate. Oricum informatia utila nu scade considerabil daca transmitem la 75% fiindca in momentul in care transmitem la 100%, multe pachete se pierd, apar coliziuni etc si trebuie retransmise.

Foarte important este de retinut ca avem control numai asupra traficului de tip egress, adica cel pe care il generam noi. Acest trafic poate fi prioritizat, impartit intre clienti, intre protocoale etc. Acest lucru se numeste "traffic shaping". Nu avem control asupra traficului de tip ingress ("traffic policing").

Pentru inceput imi propun sa explic cativa termeni utilitati si cateva concepte:

  • Trafic interactiv = trafic care trebuie sa aiba delay minim si care in general este format din doar unul sau cateva pachete. Nu ne intereseaza volumul de trafic (throughput-ul) ci doar delay-ul, timpul necesar de a ajunge de la sursa la destinatie. In aceasta categorie se incadreaza: 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 scazut, doar volumul conteaza. Ex: email, P2P, web download, ftp-data, bittorent etc
  • Coada (queue) si disciplina cozii (queueing discipline). In Linux (dar si pe alte routere dedicate (ex. cisco)), traficul care trebuie trimis catre o destinatie este salvat intr-un buffer, intr-o coada, asta fiindca in general computerele si reteaua din spatele routerului trimit date mult mai repede decat routerul sau conexiunea acestuia la Internet pot procesa. Pentru a nu pierde pachetele, routerul le pune intr-o coada. Nota: Cand spun router ma refer atat la un router dedicat cisco dar si la un calculator care ruleaza Linux si are functie de router.

Aceasta coada este de fapt un buffer in care pachetele asteapta sa fie livrate. Dimensiunea cozii este de maxim cativa MB. O coada de 3MB este considerata gigantica. Cu fiecare coada se asociaza o disciplina (queueing discipline), adica modul/ordinea in care pachetele sunt puse in coada si livrate. Acesta disciplina este de obicei by default de tip FIFO (First In First Out). Fiecare pachet asteapta ca toate cele dinaintea lui sa fie livrate pentru a-i veni randul. Pentru a maximiza bandwidth-ul acest lucru este foarte bun, dar se maximizeaza si delay-ul. In acest mod traficul interactiv are mult de suferit.

  • qdisc = queueing discipline (disciplina cozii)
  • classfull queueing qdisc = traficul se poate imparti in clase, exista clase parinte, clase copil, clase de tip leaf (clase din care nu mai deriva nimeni). Principiul este asemanator cu cel din programarea orientate obiect.
  • prio = prioritatea oferita fiecarei clase de a imprumuta de la clasa parinte. Cu cat prio este mai mica cu atat prioritatea este mai mare.
  • classless qdisc = nu exista nici o subdiviziune a traficului, totul este la gramada
  • clasa = un tip de trafic (ex. tot traficul care se duce catre yahoo.com de la un anume IP catre portul 25).
  • rate = traficul minim garantat pentru fiecare clasa
  • ceil = traficul maxim pe care are voie sa-l atinga o clasa
  • burst = cantitatea de trafic care se trimite la ceil-speed pana se trece sa se serveasca o alta clasa
  • cburst= cantitatea de trafic care se trimite la wire-speed pana se trece sa se serveasca o alta clasa

Daca o clasa nu foloseste toata banda care ii este alocata, o clasa copil poate imprumuta banda de la clasa parinte (daca aceasta are surplus de banda).

  • quantum = numarul de bytes imprumutati de parinte clasei copil pana sa imprumute unei alte clase. Nu trebuie sa fie mai mic decat MTU (1500 bytes)
  • egress = traficul expediat prin interfata externa a border router-ului (gateway). Aici intervenim.
  • ingress = traficul receptionat prin interfata externa a border router-ului (gateway). Nu putem interveni in managementul lui.
  • Traffic Shaping = ignorarea sau intirzierea pachetelor ce trebuie expediate, in vederea respectarii unei anumite rate si astfel a minimizarii cozii si respectiv delay-ului.
  • Traffic Policing = ignorarea traficului receptionat (efecte pozitive se obtin greu sau imposibil)
  • filter = imparte traficul in categorii de trafic in functie de un criteriu si il asociaza cu o clasa (ex: traficul de tip ftp in clasa 2:20).
  • HTB = Hierarchical Token Bucket. Un algoritm classfull care foloseste notiunea de token (buffer) si bucket (buffer). Este cea mai folosita si la indemana modalitate de QoS in Linux. Exemplul de fata va folosi HTB.
  • root qdisc = root qdisc este qdisc atasat direct unei interfete.

Fiecarei interfete de retea ii este atasat un qdisc. Qdisc si class sunt legate una de cealalta. Qdisc este cel care trimite traficul generat de fiecare clasa. Fiecare clasa are un qdisc.

Avand explicate majoritatea conceptelor din HTB, putem da un exemplu practic.

Exemplul 1

Presupunem ca exista un router Linux, care leaga o retea locala de mai multe calculatoare in Internet. Exista un singur IP public, iar routerul face NAT. Presupunem ca ISP ofera o legatura asimetrica de 1024 Kbs download si 256Kbs upload. Dupa un studiu amanuntit am identificat traficul generat ca fiind de tip: dns, web, ftp, ssh, email si p2p. Acest tip de trafic vrem sa-l prioritizam dupa nevoie. Vrem ca in momentul in care dc++ sau eMule ruleaza, sa primeasca doar traficul care ramane nefolosit (daca exista), iar daca nici un alt protocol nu foloseste banda vrem ca p2p sa primesca tot. Vrem ca web,ssh si dns si packetele care contin flagurile tcp ack, syn/ack, rst, fin sa aiba delay minim, astfel incat paginile sa se incarce repede chiar daca altcineva face download la un fisier mare sau trimite mailuri.

Downlodurile, ftp si mailul vrem sa aiba delay mare dar si throughput mare. P2P (kazaa, dc++, eMule, bittorent) vrem sa aiba prioritate minima.

In Linux HTB se implementeaza folosind tc (man tc). tc insemna traffic control. Toate comenzile le vom scrie intr-un script pentru o mai usoara folosire a lor. In plus scriptul va porni la bootare, imediat dupa interfata Ethernet care genereaza traficul. Pentru a clasifica traficul vom folosi iptables cu optiunea --mark. Exista si alte modalitati (ex: u32, campul tos din headerul 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 si 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
#mark web packets
iptables -A OUTPUT -t mangle -o $IF -p udp --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 udp --dport 25 -j MARK --set-mark 2
#mark pop3 packets
iptables -A OUTPUT -t mangle -o $IF -p udp --dport 110 -j MARK --set-mark 2
#mark imap packets
iptables -A OUTPUT -t mangle -o $IF -p udp --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 sa 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 si adaptat pentru orice retea mica-medie care foloseste o singura conexiune la Internet. Bineinteles ca traficul se prioritizeaza in functie de preferintele fiecaruia.

Pentru a testa ca functioneza, recomand folosirea lui iftop (http://www.ex-parrot.com/~pdw/iftop/) in timp ce se genereaza trafic. Pentru a fi clar este bine ca pe o legatura super rapida intre doua calculatoare (fast Ethernet 100Mbs) sa se simuleze o legatura extrem de inceata ca sa se observe diferenta. Se seteaza HTB si se limiteaza cateva protocoale la cativa zeci de Kbytes si se verifica in timp real cu iftop ca functioneaza corect.

Ex: limitam ftp la 10 kbit, web la 2kbit si trasferul de fisiere cu smb (Server Message Block) la 15 kbit. Apoi se incearca trasferul de fisiere mari cu ftp sau smb sau interogarea serverului web.

Exemplu 2

Presupunem ca functionam ca un mic ISP de cartier si ca vrem sa impartim banda intre 3 clienti in functie de tipul de abonament. Daca exista banda nefolosita de un client, poate fi oferita unui alt client in 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 atasam o prioritate fiecareia
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

Informatiile prezentate mai sus va pot oferi un bun start in invatarea despre QoS si HTB folosind Linux. In nici un caz nu sunt complete sau suficiente. Pot spune ca "just scratched the surface". Pentru mai multe informatii cititi 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