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