0. Wstep
Siec generuje ruch. Ruch generowany przez roznych uzytkownikow moze miec zasadniczo rozny charakter. Jeden wypelnia pasmo poprzez ogladanie www, inny uruchamia BitCometa, Edonka i co tam kto chce jeszcze generujac tym samym ruch, ktory skutecznie moze przypchac praktycznie kazde lacze. Z tego powodu powinno sie stosowac chocby najprostsza polityke ruchu.
1. Co kontrolowac?
Przede wszystkim 'download' klientow. Chcemy uzyskac maksymalna przepustowosc dla pojedynczego klienta zapewniajac przy tym innych, ze w przypadku korzystania z lacza dostana oni takze cos dla siebie. Idealna sytuacja jest taka:
1) kazdy dostaje po rownym kawaleczku pasma dla siebie - nazwijmy sobie to sobie pasmem gwarantowanym
2) w przypadku gdy ktorys klient nie korzysta z pasma - jego pasmo moze zostac przydzielone komus innemu. Tutaj mamy dynamiczny przydzial pasma - gdy siec nie jest wykorzystana w pelni - uzytkownicy moga dostac wieksza przepustowosc niz ta, ktora daje im pasmo gwarantowane.
Jednak kontrolowanie 'download`u' moze nie wystarczyc. Jeden klient moze uruchomic np. 150 polaczen i bedzie potrafil skutecznie zapchac pasmo wyjsciowe (upload). Wtedy moglibysmy sciagac tylko pod warunkiem, ze nasze zapytania wyjda na zewnatrz. Taka sytuacja zapchania uploadu moze skutecznie to uniemozliwic.
Wniosek: kontrolujmy download i upload serwera.
W przypadku downloadu kontrolowac bedziemy ruch przydzielany na adresy IP klientow. W przypadku upload mozna zastosowac dzielenie na dane uslugi (np. www+email+gg maja gwarancje X kbit reszta Y, lacznie X+Y=szybkosc lacza upload).
Notka 1:
Gdybysmy chcieli kontrolowac upload klientow wg ich numerow IP musielibysmy uzyc Linux-IMQ.
2. Wstep do skryptu?
Ogolnie schemat jest dosc prosty:
Download:
1) Zakladamy glowna kolejke na urzadzenie sieciowe, ktore bedziemy kontrolowac
Kolejka ta ma pelna predkosc interfejsu.
2) Z kolejki tej wyprowadzamy kolejne kolejki dla klientow - jedna kolejka na jednego klienta.
3) Kierujemy ruch do odpowiednich kolejek. Czyli wszystko co idzie na IP X wpada do kolejki dla tego numeru zalozonej.
Upload:
1) j.w.
2) Kolejne kolejki na grupy uslug
3) j.w. ale dla portow
HTB potrafi ograniczac predkosc tylko wysylanych danych na interfejsie. Nie potrafi samo w sobie kontrolowac i zarzadzac ruchem, ktory na ten interfejsc przychodzi. Z tego powodu klopotliwe troszeczke staje sie ograniczanie uploadu uzytkownikow, poniewaz to sa przychodzace dane do interfejsu. Zamiast tego kontrolowac mozna spokojnie wyjscie karty na swiat. Gdyby komus to nie starczylo -> notka 1.
3. Download.
Jedziemy - od razu ze skryptem.. a co. Aha - ten skrypt mozna o wiele ladniej napisac, mozna zrobic trzymanie danych userow gdzies tam w plikach czy kto jak tam chce. Mi to nie bylo potrzebne... wiec sorki, jesli komus sie nie podoba. Komentarze napisze po # to latwiej bedzie wklejac do pliku jako gotowca.
# Dane globalne skryptu:
#!/bin/sh
# download
# polecenie tc do manipulacji kolejkami...
TC="/sbin/tc"
# Interfejs wyjsciowy..np eth0 eth1 czy co tam... tutaj interfejs dla klientow sieci
IF="eth1"
#IF="bridge"
# pelna predkosc interfejsu
# tutaj mam 4190kbit (siec 802.11b - praktycznie uzyskiwane u mnie predkosci)
IFSPEED="4190kbit"
# predkosc sciagania z internetu, predkosc sieci lokalnej po odjeciu predkosci internetu
# ogolnie mamy (DSLSPEED + LANSPEED) ma sie rownac IFSPEED lub byc od niej mniejsze
DSLSPEED="625kbit"
LANSPEED="3500kbit"
# Wyliczamy predkosc na uzytkownika
# USERPSEED=DSLSPEED/ilosc_uzytkownikow
USERSPEED="125kbit"
# ADRESY serwera i uzytkownikow:
SERVER="192.168.4.1"
USER1="192.168.4.21"
USER2="192.168.4.22"
USER3="192.168.4.23"
USER4="192.168.4.24"
USER5="192.168.4.25"
#################### koniec globalnych danych ######################
# sekcja skryptu
echo "$0 start"
# czyscimy pozostalosci
$TC qdisc del root dev $IF
$TC qdisc add dev $IF root handle 1:0 htb
# zakladamy glowna kolejke na interfejs
# rate = predkosc gwarantowana kolejki
# ceil = predkosc maksymalna kolejki (w przypadku dynamicznego przydzialu to granica predkosci dla kolejki)
# pelna predkosc interfejsu...
# classid = nazwa kolejki
$TC class add dev $IF parent 1:0 classid 1:1 htb rate $IFSPEED ceil $IFSPEED
# kolejka na internet - stala predkosc kolejki rate=ceil
# parent=ktora kolejke rozgaleziamy? w tym wypadku to podkolejki kolejki glownej, ktora wyzej zalozylismy..
$TC class add dev $IF parent 1:1 classid 1:2 htb rate $DSLSPEED ceil $DSLSPEED
#kolejka na lanowe polaczenia z serwera (klient sciaga po ftp z serwera etc..)
$TC class add dev $IF parent 1:1 classid 1:3 htb rate $LANSPEED ceil $LANSPEED
# kolejki uzytkownikow na internet
# wazne zauwazyc, ze rozgaleziaja kolejke internetu (1:2)
# kazdy user ma gwarantowane USERSPEED jednak jego maksymalna szybkosc jest limitowana do DSLSPEED
$TC class add dev $IF parent 1:2 classid 1:4 htb rate $USERSPEED ceil $DSLSPEED
$TC class add dev $IF parent 1:2 classid 1:5 htb rate $USERSPEED ceil $DSLSPEED
$TC class add dev $IF parent 1:2 classid 1:6 htb rate $USERSPEED ceil $DSLSPEED
$TC class add dev $IF parent 1:2 classid 1:7 htb rate $USERSPEED ceil $DSLSPEED
$TC class add dev $IF parent 1:2 classid 1:8 htb rate $USERSPEED ceil $DSLSPEED
### filtry - kieruja odpowiednie dane do kolejek ###
# wszystkie polaczenia, ktore nawiazano z serwerem (np z jego wlasnym FTP) powinny trafic do kolejki na lan
# priorytet = w htb ostatnia pasujaca regula wygrywa dlatego warto nadac wlasny priorytet regulom
# im mniejsza liczba tym lepszy priorytet i waznosc reguly
# wszystkie polaczenia, gdzie zrodlem jest adres IP serwera zostaja skierowane do kolejki o nazwie 1:3 <- kolejkna lan
$TC filter add dev $IF protocol ip prio 1 parent 1:0 u32 match ip src $SERVER flowid 1:3
# filtry uzytkownikow
# jesli nie serwer nawiazal polaczenie, to uznajemy, ze to polaczenie z internetu
# jesli adres docelowy to adres klienta1 to dane wrzucamy mu do jego wlasnej kolejki
# tutaj mozna grupowac ludzi, np kilka adresow IP wrzucic do jednej kolejki...
# w rozwiazaniu jest 1 IP jedna kolejka
$TC filter add dev $IF protocol ip prio 2 parent 1:0 u32 match ip dst $USER1 flowid 1:4
$TC filter add dev $IF protocol ip prio 2 parent 1:0 u32 match ip dst $USER2 flowid 1:5
$TC filter add dev $IF protocol ip prio 2 parent 1:0 u32 match ip dst $USER3 flowid 1:6
$TC filter add dev $IF protocol ip prio 2 parent 1:0 u32 match ip dst $USER4 flowid 1:7
$TC filter add dev $IF protocol ip prio 2 parent 1:0 u32 match ip dst $USER5 flowid 1:8
### sfq
# sfq zapewnia sprawiedliwe dzielenie kolejki przez wiele polaczen
# np gdy uzytkownik otworzy jedna sesje sciagania z netu to mialby max predkosc kolejki
# jesli uruchomi drugie to sfq sprawi, ze te pasmo zostanie rozdzielone na 2 sprawiedliwie
# takze uzytkownik ma sprawiedliwosc w tym co dostaje na wlasnych polaczeniach.
# na lan nalozona dyscyplina - przeciez kilka osob na raz moze sciagac - chcemy zeby mieli po rowno
$TC qdisc add dev $IF parent 1:3 handle 3:0 sfq perturb 10
# dyscypliny userow
$TC qdisc add dev $IF parent 1:4 handle 4:0 sfq perturb 10
$TC qdisc add dev $IF parent 1:5 handle 5:0 sfq perturb 10
$TC qdisc add dev $IF parent 1:6 handle 6:0 sfq perturb 10
$TC qdisc add dev $IF parent 1:7 handle 7:0 sfq perturb 10
$TC qdisc add dev $IF parent 1:8 handle 8:0 sfq perturb 10
#EOF#
4. Upload.
Tutaj male zmiany w stosunku do download. Najwazniejsza, ze mamy grupy portow, dla ktorych trzymamy kolejki z gwarantowanymi predkosciami. Dodatkowo nie wazne sa adresy IP - tutaj operujemy tylko portami. Chcemy miec kilka kolejek:
1) kolejka glowna na www+email+gg....
2) kolejka na gry
3) kolejka na polaczenia z naszym serwerem (gdy ktos z naszym serwerem sie laczy np. przez www)
4) kolejka na inne pierdoly (edonki, kazy itd)
Tutaj dodamy priorytety dynamicznego przydzialu predkosci. Chcemy by najwiecej predkosci niewykorzystanej szlo na pierwsza kolejke. Co z tego zostanie powinno zostac zaoferowane pozniej grom, a jesli te jeszcze nie wykorzystaja wszystkiego to polaczeniom z naszym serwerem, a dopiero na koncu zwiekszeniu przepustowosci moglaby zostac poddana kolejka na reszte.
Do rzeczy:
#!/bin/sh
# upload
TC="/sbin/tc"
#interfejs wyjsciowy serwera (ten od netu)
IF="eth1"
IFSPEED="150kbit"
# kolejka glowna - predkosci i priorytet
MAINSPEED="35kbit"
MAINPRIO="prio 1"
# gry
GAMESSPEED="40kbit"
GAMESPRIO="prio 2"
# server
SERVSPEED="35kbit"
SERVMAX=${IFSPEED}
SERVPRIO="prio 3"
# pozostale UWAGA zmniejszona szybkosc dynamiczna kolejki - nie lubimy wysylac do innych, chcemy zapas lacza przynajmniej w dzien.
OTHERSPEED="40kbit"
OTHERMAX="90kbit"
OTHERPRIO="prio 4"
# porty zrodlowe i docelowe dla glownej kolejki
MAIN_DPORTS="22 25 53 80 110 443 1550 2796 8074 1911 4569 8000 5222 5223 5269 33434"
MAIN_SPORTS="22 53"
# dla gier...
GAMES_DPORTS="2796 3658 5003 27960 27961 27015 6112 6113 6114 6115 6116 6117 6118 6119 27910 23400 28960 28961"
# i serwera
SERV_SPORTS="21 25 80 110 6667"
#### skrypt #####
echo "$0 start"
$TC qdisc del root dev $IF
# r2q - rate to quantum - przelicznik dla htb, GDY MAMY MALY UPLOAD TRZEBA GO ZMNIEJSZYC do np 1. Standardowo jest 10 i pomija sie
# default 5 = wszystko co w filtrach nie zostanie skierowane do ktorejs kolejki zostanie automatycznie skierowane do kolejki 1:5
$TC qdisc add dev $IF root handle 1:0 htb r2q 1 default 5
$TC class add dev $IF parent 1:0 classid 1:1 htb rate $IFSPEED ceil $IFSPEED
# dodane tylko priorytety "sciagania nadmiarow niewykorzystanych przez inne kolejki"
$TC class add dev $IF parent 1:1 classid 1:2 htb rate $MAINSPEED ceil $IFSPEED $MAINPRIO
$TC class add dev $IF parent 1:1 classid 1:3 htb rate $SERVSPEED ceil $SERVMAX $SERVPRIO
$TC class add dev $IF parent 1:1 classid 1:4 htb rate $GAMESSPEED ceil $IFSPEED $GAMESPRIO
$TC class add dev $IF parent 1:1 classid 1:5 htb rate $OTHERSPEED ceil $OTHERMAX $OTHERPRIO
# kolejka glowna
# icmp chcemy takze tu wrzucac (male pingi=fajna rzecz)
$TC filter add dev $IF protocol ip parent 1:0 u32 match ip protocol 1 0xff flowid 1:2
# dport= destination port - port docelowy polaczenia
# 0xffff = nie patrzec na TOS pakietow...
for i in $MAIN_DPORTS; do
$TC filter add dev $IF protocol ip parent 1:0 u32 match ip dport $i 0xffff flowid 1:2
done
for i in $MAIN_SPORTS; do
$TC filter add dev $IF protocol ip parent 1:0 u32 match ip sport $i 0xffff flowid 1:2
done
# serwer
for i in $SERV_SPORTS; do
$TC filter add dev $IF protocol ip parent 1:0 u32 match ip sport $i 0xffff flowid 1:3
done
# gry
for i in $GAMES_DPORTS; do
$TC filter add dev $IF protocol ip parent 1:0 u32 match ip dport $i 0xffff flowid 1:4
done
# sfq dla kolejek
$TC qdisc add dev $IF parent 1:2 handle 2:0 sfq perturb 10
$TC qdisc add dev $IF parent 1:3 handle 3:0 sfq perturb 10
$TC qdisc add dev $IF parent 1:4 handle 4:0 sfq perturb 10
$TC qdisc add dev $IF parent 1:5 handle 5:0 sfq perturb 15
#EOF#
5. Podsumowanie
Najlepiej jest utworzyc jedna wersje skryptow na dzien i druga na noc. Ta na noc niech bedzie mniej rygorystyczna.
Gdybysmy chcieli operowac HTB na 2 karty sieciowe dla klientow: np. 192.168.1.x i 192.168.2.x to htb nie pozwoli standardowo zrobic tego, poniewaz kolejki zakladane sa na interfejs a nie na adresy. Stad 192.168.1.0=eth1 a 192.168.2.0=eth2. Mamy 2 urzadzenia i 2 kolejki glowne - mozemy podzielic lacze na 2 i w ramach takiego podzialu serwowac ograniczenia. Jednak to slabe podejscie, bo maksymalnie mozna dynamicznie wtedy przy sprawiedliwym podziale uzyskac 1/2 lacza. A tego nie chcemy:( Wyjsciem jest albo Linux-IMQ albo zastosowanie bridge`a. Z wlasnego doswiadczenia wiem, ze bridge zrobic jest prosciej i szybciej i dla 2 segmentow sprawdza sie wysmienicie. Gdy kart wiecej to chyba warto IMQ sie zainteresowac.
Jesli juz sie cos dzieli to powinno sie ograniczyc ilosc polaczen uzytkownika. Doradzam patcha 'connlimit' z POM dla iptables.
W opisie moga byc bledy, jesli tak, to przepraszam.. staralem sie nie nawalic bugow

pozdrawiam
Marek K.