Debian - HTB + IMQ (Warszawa dn. 8.VII.2004)
Author: Rafal Rajs (ElessaR) (email: elessar1@poczta.wp.pl)
Ostrzeżenie 1
UPDATE 10.I.2007
Niestety łata IMQ Jiri Fojtaska nie jest już dostępna. Tylko ona gwarantowała stabilne działanie IMQ w konfiguracji przedstawionej w poniższym artykule. Używając standardowych łat z www.linuximq.net prawdopodobieństwo wystąpienia Kernel Panic'a jest bardzo duże, szczególnie jeśli używany jest shaping ruchu lokalnego serwera. Z przykrością muszę zawiadomić zatem, że poniższy artykuł przestał być aktualny. Zapraszam do lektury mojego nowego artykułu o traffic shapingu na Linuxie. Tym razem w oparciu o IMQ i HFSC.
Ostrzeżenie 2
UPDATE 21.IX.2004
Patch IMQ pochodzący linuximq.net powoduje niestety KERNEL PANIC. Z tego co udało mi się ustalić dzieje to się w przypadku gdy do jednego urządzenia IMQ kierujemy pakiety z łańcuchów PRE i POST-ROUTING z 1 interfejsu fizycznego jednocześnie.
Rozwiązaniem kłopotów stało się zainstalowanie patcha IMQ innego autorstwa niż oryginalny. Łata IMQ autorstwa Jiri Fojtasek z http://hyperfighter.jinak.cz/qos/ jest o wiele bardziej stabilna.
ZAŁOŻENIA
[Poniższy tekst prezentuje wyłącznie moje zdanie i jest głównie oparty na własnych doświadczeniach. Jeśli tekst zawiera błędy, bedę wdzięczny za powiadomienie.]
Witajcie.
Postanowiłem w końcu zoptymalizować limity pasma na lączu DSL 512kb TPSA. System Debian Woody.
Można się zapytać "A po co to wszystko? Przecież w internecie jest tyle tutoriali na temat limitów pasma.".
Niestety żaden z tych dokumentów nie spełnił moich oczekiwań m.in. dlatego że:
- przykłady i rozwiązania bazowały tylko i wyłącznie na limitach ruchu wychodzącego
- brak rozwiązań kompleksowych z IMQ
- korzystanie ze źródel i instalacja rozwiązań prosto z nich, co powoduje brak spójności w systemie.
Będę starał się rozwiązać te problemy. Zaproponuje:
- kompleksowe/zautomatyzowane rozwiązanie limitu ruchu wychodzącego i wchodzącego TYLKO na interfejsie
zewnętrznym opartego o HTB, IMQ
- tworzenie pakietów binarnych zmodyfikowanych elementów systemu tj. jądra, iptables, iproute, dzieki czemu
łatwo możemy kontrolować jaka wersja i gdzie jest zainstalowana nasza modyfikacja.
TROCHĘ ROZWAŻAŃ
Zawsze zastanawiał mnie bowiem problem:
Najcześciej kontroluje sie ruch wychodzacy z interfejsu. W rozwiązaniach standardowych CBQ, HTB sensownie
możemy limitować
tylko ruch wychodzący z interfejsu. Oznacza to jednak, iż musimy tworzyć 2 główne kolejki przypisane interfejsom
wewnętrznemu i zewnętrznemu, nadając im limit pasma oddzielnie i w sposób zupełnie niezależny od siebie.
Nie mamy możliwosci wspólnego kontrolowania ruchu wchodzącego i wychodzącego, a przecież przepustowość łącza
zawiera w sobie obie te wartości.
Dlatego można sobie wyobrazić pewną sytuację:
Mamy łącze do netu 2Mbps. Aby wykorzystanie łącza było możliwe w 100%. Kolejki główne muszą być równe 2Mbps.
ROOT1:ETH0:limit 2Mbps ROOT2:ETH1:limit 2Mbps
LAN <------------------------------------ ROUTER --------------------------------------> INTERNET
Jeśli userzy głównie sciągaja z netu, nie powinniśmy zauważyć większych problemów. Jednak jeśli userzy coś do netu WYSYŁAJĄ. Co się wtedy dzieje? Rzeczywista przepustowość łącza maleje, natomiast kolejka ROOT1 dalej uważa ze ma wolne 2 Mbps, dzieli w ten sam sposób pasma, przez co userzy mają większe pasma niż powinni i z łatwoscią przeciążają łącze, a skuteczność limitu znacznie maleje (chodzi tu głównie o ruch wymagający niskich opóźnień). W takiej sytuacji idea limitu pasma przestaje być atrakcyjna.
Jednak jest bardzo skuteczne rozwiązanie ... IMQ. Dzięki temu urządzeniu możemy skierować ruch wchodzący i wychodzący do urządzenia IMQ, nadać mu kolejkę i ustawić limit na przepustowość łącza. Teraz problem z przeciążaniem łącza zniknie. Przydałoby się kontrolować bezposrednio interfejs zewnętrzny. W takim przypadku bardzo łatwo okreslić kto korzysta z internetu, a nie z zasobów LAN, czy serwera/bramki. Niestety przy zastosowaniu NAT pojawiają się nie lada problemy przy wykorzystaniu tylko i wyłącznie interfejsu zewnętrznego. Natowane pakiety mają zmienione adres źródłowy/docelowy, przez co nie możemy ich odpowiednio rozdzielić do kolejek z limitami.
Na szczęście można ten problem rozwiazać. :)
INSTALACJA
Zatem zaczynamy:
Oczywiście najpierw sciągnąłem źródeł:
1. Kernel 2.4.26 ( http://www.kernel.org )
2. Patch IMQ na kernel 2.4.26
3. Patch IMQ na IPTABLES
4. Patch poprawiające działanie IMQ przy natowanych sieciach
5. Najnowsza paczka źródłowa Debiana IPTABLES (
1,
2 ) | http://www.debian.org/distrib/packages#search_packages
6. Najnowsza paczka źródłowa Debiana IPROUTE (
1,
2,
3 )
| moga byc wersje niestabilne
KERNEL
solaris#cd /usr/src
solaris#tar -jxf linux-2.4.26.tar.bz2
solaris#ln -s linux-2.4.26 linux
Patch IMQ:
solaris#cd linux
solaris#patch -p1 < ../linux-2.4.26-imq.diff
solaris#cd drivers/net
solaris#patch < ../../../imqnat.diff
solaris#cd ../../
KOMPILACJA
solaris#cp twoj_sprawdzony_kernel_config .config
solaris#make menuconfig
zaznaczamy m.in:
# Networking options ---> Network packet filtering (replaces ipchains)
# Networking options ---> IP: Netfilter Configuration ---> IP tables support (required for filtering/masq/NAT)
# Networking options ---> IP: Netfilter Configuration ---> Packet mangling
# Networking options ---> IP: Netfilter Configuration ---> IMQ target support
# Network device support ---> IMQ (intermediate queueing device) support
solaris#make-kpkg clean
solaris#make-kpkg --append_to_version -486.imq.by.elessar --revision=rev.01 debian (utworzy nam katalog debian)
solaris#dch (dodajemy wpis w changelog)
solaris#make-kpkg --append_to_version -486.imq.by.elessar --revision=rev.01 kernel_image
solaris#cd ..
solaris#dpkg -i kernel-image-2.4.26-486.imq.by.elessar.rev01_i386.deb
solaris#(odpowiednie wpisy w /etc/lilo.conf)
solaris#lilo -v
solaris#reboot
IPTABLES
solaris#dpkg-source -x iptables_1.2.9-10.dsc
solaris#cd iptables-1.2.9
solaris#dch -v 1.2.9-10.imq
solaris#dpkg-buildpackage -us -uc
solaris#cd debian/build/iptables-1.2.9
solaris#patch -p1 < ../../../../iptables-1.2.9-imq1.diff
solaris#cd extensions/
solaris#chmod +x .IMQ-test*
solaris#pico Makefile ( do PF_EXT_SLIB na końcu dodajemy IMQ )
solaris#make
solaris#cd ../../../
solaris#dpkg-buildpackage -us -uc -nc
Powtarzamy budowanie pakietu tym razem jednak z opcja [-nc], aby skrypt nie usunął naszych zmian.
solaris#cd ..
solaris#dpkg -c iptables_1.2.9-10.imq_i386.deb | grep IMQ
Sprawdzamy czy pakiet na pewno zawiera bibliotekę IMQ.
solaris#dpkg -i iptables_1.2.9-10.imq_i386.deb
IPROUTE
Budujemy własną paczkę ze źródeł, ponieważ wersja binarna pochodzi z dystrybucji NIESTABILNEJ, przez co byśmy mieli problemy z zależnościami między pakietami.
solaris#dpkg-source -x iproute_20010824-13.1.dsc
solaris#cd iproute-20010824
solaris#pico debian/rules
[[[ odszukujemy linie
$(MAKE) KERNEL_INCLUDE=/usr/include
i zamieniamy ją na:
$(MAKE) ]]]
solaris#dpkg-buildpackage -us -uc
solaris#cd ..
solaris#dpkg -i iproute_20010824-13.1_i386.deb
KONFIGURACJA
Mamy zatem już zainstalowane własne wersje pakietów binarnych, które są koniecznie do działania HTB i IMQ. Możemy zająć sie wreszcie ustawianiem limitów.
Utworzyłem 3 pliki konfiguracyjne (/etc/skrypty/):
- htb
- firewall
- nat
Wszystkie te pliki korzystają z ustawień globalnych zawartych w pliku globals. To własnie ten plik jest przeznaczony do edycji przez administratora.
Oto odpowiednie skrypty uruchomieniowe umieszczone w /etc/init.d/:
- htb
- firewall
- nat
ZASADA DZIAŁANIA
Dla wygody użytkownia wprowadziłem kilka definicji globalnych:
- kilka definicji oczywistych ;)
ZEW=eth1 #
WEW=eth0 #LAN
LAN="172.16.1.0/24"
IP_ZEW="10.1.1.100"
- w tym także kilka istotnych
L_USERS=5 #liczba userow w sieci!!!
USER_IP[1]="172.16.1.2"
USER_IP[2]="172.16.1.10"
USER_IP[3]="172.16.1.11"
USER_IP[4]="172.16.1.12"
USER_IP[5]=$IP_ZEW # ostatni adres to adres zewnetrzengo interfejsu serwera
Jeśli zamierzamy dodać kolejnego usera robimy to w następujący sposób:
L_USERS=6 #liczba userow w sieci!!!
USER_IP[1]="172.16.1.2"
USER_IP[2]="172.16.1.10"
USER_IP[3]="172.16.1.11"
USER_IP[4]="172.16.1.12"
USER_IP[5]="172.16.1.20" (nowy adres)
USER_IP[6]=$IP_ZEW # ostatni adres to adres zewnetrzengo interfejsu serwera
Po restarcie skryptu HTB powstaną nowe kolejki oraz nowe limity pasma dostosowane do liczby userów.
- a także wręcz o krytycznych znaczeniu!
MAX_UPLOAD=128
MAX_DOWNLOAD=512
MAX_TRANSFER=512
To właśnie te powyższe zmienne decydują o przepustowości głównych kolejek. Możemy je dopasować do własnych potrzeb. Download i upload został rozdzielony na potrzeby łącz ADSL, ostatnio bardzo popularnych. Powyższe dane są odpowiednie dla DSL 512 oferowanego przez TPSA. Transfery zostały potwierdzone doświadczalnie.
Limity ustaliłem w następujący sposób. Każdy user dostaje swoją część downloadu i uploadu na podstawie
adresu IP. Wyliczane to jest tak:
# upload na kazdego usera
USER_UPLOAD=$[$MAX_UPLOAD/$L_USERS]
# download na kazdego usera
USER_DOWNLOAD=$[$MAX_DOWNLOAD/$L_USERS]
W każdej klasie dla usera (IP) wydzielam 2 pasma. Ruch ważniejszy i reszta. Dzięki temu można ustawić sobie priorytety dla usług typu SSH, poczta, Counter-Strike. Aby to zrobic wystarczy dodać kilka prostych regułek. W tej chwili do ruchu ważniejszego zaliczane są pingi.
Ruch ważniejszy stanowi 75% pasma usera:
USER_D_PRIOR=$[$USER_DOWNLOAD*75/100]
USER_D_RESZTA=$[$USER_DOWNLOAD-$USER_D_PRIOR]
USER_U_PRIOR=$[$USER_UPLOAD*75/100]
USER_U_RESZTA=$[$USER_UPLOAD-$USER_U_PRIOR]
W jaki sposób kierujemy pakietu do kolejek?
Na początek rzecz prosta:
$IPTABLES -t mangle -A POSTROUTING -o $ZEW -j IMQ
$IPTABLES -t mangle -A PREROUTING -i $ZEW -j IMQ
Cały ruch z interfejsu zewnętrznego przekierowujemy do naszego urządzenia IMQ.
Jednak tutaj zaczynają się schody. Jak rozróżnić pakiety z sieci LAN? Przecież powinny być w tej chwili już z SNATowane i mieć adres źródłowy interfejsu zewnętrznego. Podobnie jest z pakietami do sieci LAN, ich adresowem docelowym jest adres IP interfejsu zewnętrznego.
Na szczęścia dzięki patchowi (4.) ruch do sieci LAN może być wyróżniony. Łata ta powoduje, iż pakiety z
PREROUTING na interfejsie $ZEW trafiają do IMQ już po z DNATowaniu, czyli mają już prawidłowe adresy
docelowe z sieci LAN. Dzięki filtrom TC rozdzielamy ruch:
#download waznego ruchu dla usera ( mozna dodac SSH, WWW, CS etc)
$TCF prio 2 u32 match ip protocol 1 0xff match ip dst ${USER_IP[${i}]} flowid 1:$[$N_CLASS_D_1+$i]
#download ogolny dla usera
$TCF prio 20 u32 match ip dst ${USER_IP[${i}]} flowid 1:$[$N_CLASS_D_2+$i]
Niestety, rozgraniczyć ruchu upload w ten sposób się nie da. Pakiety z POSTROUTING trafiają do IMQ juz SNATowane. Możemy jednak wyróżnić pakiety MARKiem w IPTABLES!
#upload ogolny dla usera
$IPTABLES -t mangle -A POSTROUTING -o $ZEW --src ${USER_IP[${i}]} -j MARK --set-mark $[$MARK_START+$j+1]
# upload waznego ruchu dla usera (mozna tutaj dodoac kolejne regulki ... mark pozostaje ten sam
# ale do klasy mozna wrzucic poczte CS ... SSH .. etc
$IPTABLES -t mangle -A POSTROUTING -o $ZEW -p icmp --icmp-type echo-request --src ${USER_IP[${i}]} -j MARK
--set-mark $[$MARK_START+$j]
$IPTABLES -t mangle -A POSTROUTING -o $ZEW -p icmp --icmp-type echo-reply --src ${USER_IP[${i}]} -j MARK
--set-mark $[$MARK_START+$j]
Pakiety są wtedy jeszcze przed translacją i mają źródłowe adresy LAN. W ten sposób zamarkowane pakiety
kieruje do odpowiednich kolejek dzięki filtrom TC:
$TCF prio 1 handle $[$MARK_START+$j] fw classid 1:$[$N_CLASS_U_1+$i] # upload (ruch wazniejszy)
$TCF prio 1 handle $[$MARK_START+$j+1] fw classid 1:$[$N_CLASS_U_2+$i] # upload (reszta)
To wszystko. Przeprowadziłem sporo testów. Pakiety trafiały do odpowiednich kolejek. Próbowałem przeciążać łącze 1/kilkoma userami, w tym czasie sprawdzałem jakie połączenie z NETem ma kolejny user. Regułki spisywały się znakomicie. Życzę owocnej pracy z limitami pasma.
Powodzenia
ELessaR