Ograniczanie pasma internetowego za pomocą IMQ i 7Layer Łukasz Antoniak L.Antoniak@stud.elka.pw.edu.pl www.antoniak.glt.pl 03 sierpnia 2007 Streszczenie Odwiecznym problemem wszystkich dzielących łącze internetowe z sąsiadem jest sprawiedliwy podział pasma. Zawsze denerwujacą jest sytuacja gdy ten drugi korzysta z sieci p2p caly dzień, podczas gdy my nie możemy sprawdzić czegoś szybko na WWW. Rozwiązanie tego problemu znajdziesz właśnie w tym artykule.
2 Ograniczanie pasma internetowego za pomocą IMQ i 7Layer Spis treści 1 Co to jest IMQ i 7Layer? 3 2 Instalacja IMQ i 7Layer 4 2.1 Co będzie nam potrzebne?.................................... 4 2.2 Nakładanie łat i instalacja jądra................................ 4 3 Przykładowy podział pasma 6 3.1 Podział ze względu na IP.................................... 6 3.2 Podział ze względu na usługi.................................. 8 4 Zakończenie 10
3 Ograniczanie pasma internetowego za pomocą IMQ i 7Layer 1 Co to jest IMQ i 7Layer? IMQ jest to pośrednie urządzenie kolejkujące, coś jak wirtualny interfejs sieciowy. Różni się on jednak od tradycyjnych eth czy ppp: 1. nie ma adresu sprzętowego, a więc nie działają na nim protokóły ARP czy RARP. 2. nie posiada adresu IP. 3. cały ruch wchodzący do tego interfejsu jednocześnie go opuszcza. IMQ umożliwia nam ograniczenie ruchu wychodzącego, jak i wchodzącego do rzeczywistego interfejsu (np. eth). Bez tego modułu nie jesteśmy w stanie ograniczyć ruchu wchodzącego do urządzeń sieciowych. Jego funkcjonalność ukazuje następujący przypadek: Rysunek 1: Przykład podziału łącza. Na serwerze działa jakaś usługa, np. ftp, p2p (ponieważ serwer jest włączony non stop, toz przyczyn ekonomicznych, chcemy uruchamiać długotrwałe ściąganie plików właśnie na nim). Działając jedynie na pakiety wychodzące przez eth0 (bez IMQ) nie jesteśmy w stanie ograniczyć w pożądany sposób pasma. Dobrym rozwiązaniem jest tutaj założenie kolejki uploadui downloadu na interfejsie zewnętrznym. 7Layer jest to patch na jądro i iptables. Umożliwia on rozpoznawanie popularnych protokołów. Nie dotyczy to jedynie usług p2p, ale takĺźe ssh, http, ftp itp. Dzięki niemu możemy w łatwy sposób rozpoznać pakiety przechodzące przez niestandardowe porty (np. ftp na porcie 20000).
4 Ograniczanie pasma internetowego za pomocą IMQ i 7Layer 2 Instalacja IMQ i 7Layer 2.1 Co będzie nam potrzebne? 1. Źródła kernela (www.kernel.org). 2. Źródła iptables (www.netfilter.org). 3. 7Layer - patch i definicje protokołów (sourceforge.net/project/showfiles.php?group id=80085). 4. IMQ - patch na jądro, NAT (na samym dole strony) i iptables (www.linuximq.net/patches.html). Ponadto musimy mieć zainstalowane wszystkie niezbędne komponenty do korzystania z HTB. Standardowo są one wkompilowane w jądro. 2.2 Nakładanie łat i instalacja jądra Wszystkie potrzebne pakiety ściągamy do katalogu /usr/src (np. programem wget) oraz rozpakowujemy je. Dla ułatwienia robimy linki symboliczne (będąc w /usr/src/ ): ln -s /usr/src/linux-2.6.15.3 linux ln -s /usr/src/iptables-1.3.5 iptables ln -s /usr/src/netfilter-layer7-v2.1 layer7 Kopiujemy patche IMQ i 7Layer do /usr/src/linux/, a następnie łatamy jądro: cp /usr/src/linux-2.6.14-imq6.diff /usr/src/linux cp /usr/src/layer7/kernel-2.6.13-2.6.15-layer7-2.1.patch /usr/src/linux cd /usr/src/linux patch -p1 < linux-2.6.14-imq6.diff patch -p1 < kernel-2.6.13-2.6.15-layer7-2.1.patch Teraz kolej na iptables. Do katalogu /usr/src/iptables/ kopiujemy patch IMQ i 7Layer, a następnie nakładamy łaty: cp /usr/src/layer7/iptables-layer7-2.1.patch /usr/src/iptables cp /usr/src/iptables-1.3.0-imq1.diff /usr/src/iptables cd /usr/src/iptables patch -p1 < iptables-layer7-2.1.patch patch -p1 < iptables-1.3.0-imq1.diff chmod +x extensions/.layer7-test cd extensions chmod 0755.IMQ* Musimy także umożliwić współprace IMQ z NAT (w kernelach 2.6.x nie jest to konieczne). Nakładamy więc patch: cp /usr/src/imq-nat.diff /usr/src/linux/drivers/net cd /usr/src/linux/drivers/net/ patch < imq-nat.dif W przypadku, gdy wystąpi błąd:
5 Ograniczanie pasma internetowego za pomocą IMQ i 7Layer patching file imq.c Hunk #1 FAILED at 37. Hunk #2 FAILED at 54. 2 out of 2 hunks FAILED -- saving rejects to file imq.c.rej należy otworzyć plik imq.c.rej i sprawdzić co poszło nie tak. W większości przypadkśw wystarczy zmieniś w pliku imq.c: NF_IP_PRI_MANGLE + 1 na NF_IP_PRI_NAT_DST + 1 NF_IP6_PRI_MANGLE + 1 na NF_IP6_PRI_NAT_DST + 1 Po tej operacji nie musimy nakładać łaty na NAT. To już koniec patchowania. Teraz możemy zająć się kompilacją jądra. Przechodzimy do katalogu /usr/src/linux/. Wydajemy komendę make menuconfig (lub make xconfig jeśli jesteśmy pod X-ami). Jeżeli mamy już zrobiony plik.config, to wczytujemy go teraz. W innym wypadku przechodząc przez kolejne kategorie wybieramy pakiety nam potrzebne. Aby uaktywnić 7Layer musimy wybrać następujące składniki: Code maturity level options Prompt for development and/or incomplete code/drivers Device Drivers Networking device support Networking Options Network packet filtering Networking Networking options Network packet filtering IP: Netfilter Configuration Connection tracking Networking Networking options Network packet filtering IP: Netfilter Configuration Connection tracking flow accounting Networking Networking options Network packet filtering IP: Netfilter Configuration Layer 7 match support Aby uaktywnić IMQ: Device Drivers Network device support IMQ (intermediate queueing device) support Tutaj trzeba się na chwilę zatrzymać. Po zaznaczeniu tego pakietu pojawią się dwie opcje: IMQ behavior i Number of IMQ devices. Liczbę wirtualnych urządzeń należy ustawić wedle własnych potrzeb. Jeśli chodzi jednak o drugą pozycję, to proponuje najpierw zapoznać się z poniższą tabelką: PREROUTING POSTROUTING AA (A)fter NAT (A)fter NAT AB (A)fter NAT (B)efore NAT BA (B)efore NAT (A)fter NAT BB (B)efore NAT (B)efore NAT Tablica 1: Wszystkie możliwości parametru IMQ behavior. Dla przykładu, jeśli wybierzemy opcje AB to w łańcuchu PREROUTING pakiety będą przepuszczane przez IMQ po translacji NAT, natomiast w łańcuchu POSTROUTING przed translacją NAT. Domyślna wartość w tym miejscu to BA. Jeśli planujemy użycie u32 match wybór którejś z tych opcji ma duże znaczenie przy pisaniu skryptu dzielącego pasmo (zastanie to wyjaśnione później). W sekcji 3 (Przykładowy podział pasma) przedstawiłem 2 rodzaje podziału: względem IP oraz względem usług. W przypadku podziału per IP należy wybrać tutaj opcję AB. Jeśli masz wątpliwości związane z kolejnością łańcuchów iptables, to na stronie www.docum.org/docum.org/kptd/ pokazana jest droga pakietu z ustawionym IMQ na BA. Wracając do konfiguracji jądra:
6 Ograniczanie pasma internetowego za pomocą IMQ i 7Layer Networking Networking options Network packet filtering (replaces ipchains) IP: Netfilter Configuration IMQ target support Networking Networking options Network packet filtering (replaces ipchains) IPv6: Netfilter Configuration IMQ target support Po zaznaczeniu niezbędnych opcji zapisujemy plik.config. Następnie tworzymy obraz jądra oraz instalujemy moduły: make bzimage make modules make modules_install Nasze nowe jądro znajduje się teraz w /usr/src/linux/arch/i386/boot/bzimage. Skopiujmy je do katalogu /boot: cp /usr/src/linux/arch/i386/boot/bzimage /boot Zwróćmy uwagę, czy ma ono odpowiednią nazwę: vmlinuz-2.6.15.3 (zmieniamy ją z bzimage). Oczywiście nie nadpisujemy naszego starego jądra, a także nie pozbywamy się go od razu. Najpierw musimy upewnić się czy nowe działa bez zarzutów. Tak więc dodajemy odpowiedni wpis do pliku konfiguracyjnego LILO /etc/lilo.conf. Przykładowo: image = /boot/vmlinuz-2.6.15.3 root = /dev/hda6 label = Slack-2.6.15.3 read-only Po dodaniu wpisu do lilo.conf uruchamiamy je ponownie w celu zapisania zmian, a więc z konsoli wykonujemy polecenie lilo. Musisz zobaczyć swój nowy wpis, u mnie - Slack-2.6.15.3. Jeśli go nie ma to wróć do pliku lilo.conf. W przypadku gdy wszystko przebiegło poprawnie, zrestartuj komputer i przy ponownym uruchamianiu załaduj nowe jądro. Ostatni etap instalacji dotyczy iptables. Przejdź do katalogu /usr/src/iptables, a następnie wykonaj polecenia: make KERNEL_DIR=/usr/src/linux make install KERNEL_DIR=/usr/src/linux Teraz już pozostało nam tylko rozpakować protokoły 7Layer (w moim przypadku /usr/src/l7-protocols- 2006-02-12.tar.gz) do wcześniej utworzonego katalogu /etc/l7-protocols. 3 Przykładowy podział pasma Dwa główne rodzaje podziału pasma to: ze względu na usługi (inna prędkość dla ftp, inna dla http etc.) oraz ze względu na źródło pakietu (konkretne IP). Częściej używana jest to druga opcja - dwóch sąsiadów dzieli sobie pasmo po równo między swoje komputery. 3.1 Podział ze względu na IP Załóżmy, że nasza sytuacja wygląda tak jak ta przedstawiona na rysunku 1. Mamy ponadto do dyspozycji łącze o uploadzie 256 kb/s i downloadzie 512 kb/s. Chcemy podzielić je równo pomiędzy 3 komputery. Skrypt dzielący pasmo może wyglądać na przykład tak:
7 Ograniczanie pasma internetowego za pomocą IMQ i 7Layer #!/bin/bash ifc_zew="eth0" 1 komputer1="192.168.2.2/24" komputer2="192.168.2.3/24" komputer3="192.168.2.4/24" max_upload="250kbit" upload_user="50kbit" upload_serv="70kbit" max_download="510kbit" download_user="127kbit" download_serv="127kbit" ########## Upload ########## ip link set imq0 up 2 tc qdisc del dev imq0 root 3 tc qdisc add dev imq0 root handle 1: htb default 10 4 tc class add dev imq0 parent 1: classid 1:1 htb rate $max_upload ceil $max_upload 5 tc class add dev imq0 parent 1:1 classid 1:10 htb rate $upload_serv ceil $max_upload 6 tc class add dev imq0 parent 1:1 classid 1:20 htb rate $upload_user ceil $max_upload tc class add dev imq0 parent 1:1 classid 1:30 htb rate $upload_user ceil $max_upload tc class add dev imq0 parent 1:1 classid 1:40 htb rate $upload_user ceil $max_upload tc filter add dev imq0 parent 1: protocol ip prio 1 u32 match ip src $komputer1 \ flowid 1:20 7 tc filter add dev imq0 parent 1: protocol ip prio 1 u32 match ip src $komputer2 \ flowid 1:30 tc filter add dev imq0 parent 1: protocol ip prio 1 u32 match ip src $komputer3 \ flowid 1:40 tc qdisc add dev imq0 parent 1:10 handle 10:0 sfq perturb 10 8 tc qdisc add dev imq0 parent 1:20 handle 20:0 sfq perturb 10 tc qdisc add dev imq0 parent 1:30 handle 30:0 sfq perturb 10 tc qdisc add dev imq0 parent 1:40 handle 40:0 sfq perturb 10 1 Nazwa interfejsu zewnętrznego. 2 Podnosi nowy wirtualny interfejs. 3 Usuwa kolejkę główną z urządzenia imq0 (jeśli takowa była). 4 Tworzy główną kolejkę na interfejs imq0. Wartość default mówi, do której kolejki zakwalifikować pakiety, które nie pasują do żadnej innej (w naszym przypadku jest to kolejka serwera 1:10). Zakładam, że zewnętrzne IP serwera jest zmienne. Nie możemy więc markować pakietów przy użyciu iptables i opcji -s, gdyż wartość source nie jest stała. 5 Tworzy kolejkę o identyfikatorze 1:1 podpiętą do kolejki głównej (parent 1:). Kolejka ta ma gwarantowaną prędkość max upload. 6 Analogicznie do poprzedniego wiersza. Ta kolejka ma jednak gwarantowaną prędkość upload serv i maksymalną max upload. Prędkość gwarantowana zostaje zwiększona, gdy jakieś inne pasmo nie jest wykorzystywane. 7 Jeśli jakiś pakiet został wysłany z adresu $serwer to zostanie on wrzucony do kolejki 1:10. Opcja prio mówi którą pozycje zajmuje dana kolejka w przydziale nieużywanego pasma. Im większa liczba tym bardziej odległe miejsce. 8 SFQ jest to algorytm zarządzający kolejką. Domyślnie każda kolejka działa jak FIFO (first in first out). Zalecane jest jednak użycie SFQ, gdyż kolejka nie zostanie wtedy zapchana pakietami jednego typu. Dla przykładu: gdy użytkownik uruchomi klienta p2p, to przy kolejce FIFO, strony WWW prawdopodobnie nie będą się w ogóle otwierać. SFQ chroni nas przed takim wypadkiem.
8 Ograniczanie pasma internetowego za pomocą IMQ i 7Layer iptables -t mangle -A POSTROUTING -o $ifc_zew -j IMQ --todev 0 9 ########## Download ########## ip link set imq1 up tc qdisc del dev imq1 root tc qdisc add dev imq1 root handle 1: htb default 10 tc class add dev imq1 parent 1: classid 1:1 htb rate $max_download ceil $max_download tc class add dev imq1 parent 1:1 classid 1:10 htb rate $download_serv ceil \ $max_download burst 5k 10 tc class add dev imq1 parent 1:1 classid 1:20 htb rate $download_user ceil \ $max_download burst 5k tc class add dev imq1 parent 1:1 classid 1:30 htb rate $download_user ceil \ $max_download burst 5k tc class add dev imq1 parent 1:1 classid 1:40 htb rate $download_user ceil \ $max_download burst 5k tc filter add dev imq1 parent 1: protocol ip prio 1 u32 match ip dst $komputer1 \ flowid 1:20 tc filter add dev imq1 parent 1: protocol ip prio 1 u32 match ip dst $komputer2 \ flowid 1:30 tc filter add dev imq1 parent 1: protocol ip prio 1 u32 match ip dst $komputer3 \ flowid 1:40 tc qdisc add dev imq1 parent 1:10 handle 10:0 sfq perturb 10 tc qdisc add dev imq1 parent 1:20 handle 20:0 sfq perturb 10 tc qdisc add dev imq1 parent 1:30 handle 30:0 sfq perturb 10 tc qdisc add dev imq1 parent 1:40 handle 40:0 sfq perturb 10 iptables -t mangle -A PREROUTING -i $ifc_zew -j IMQ --todev 1 11 Nałożyliśmy więc kolejkę uploadu i downloadu na eth0. W przypadku korzystania z tych skryptów odnośniki do komentarzy należy oczywiście usunąć. Jak widać jest to zwykły skrypt HTB z użyciem interfejsów wirtualnych (imq0 i imq1 ). W razie jakichkolwiek wątpliwości odnośnie składni komend polecam zapoznanie się z zagadniem HTB. Należy zwrócić szczególną uwagę na przekierowanie pakietów do urządzeń wirtualnych. 3.2 Podział ze względu na usługi Teraz kolej na podział pasma ze względu na usługi (poza IMQ użyjemy tutaj 7Layer): #!/bin/bash ifc_zew="eth0" max_upload="60kbit" up_others="5kbit" up_ssh="15kbit" up_http="20kbit" 9 Każdy pakiet wychodzący przez interfejs zewnętrzny przechodzi do urządzenia imq0, na którym jest kolejkowany. 10 Znacznik burst mówi jak duży pakiet może zostać ściągnięty bez ograniczenia pasma (tutaj 5 kbit). Dzięki tej opcji niewielkie strony WWW ładują się szybciej. 11 Każdy pakiet przychodzący z Internetu do LAN-u kolejkowany jest w imq1.
9 Ograniczanie pasma internetowego za pomocą IMQ i 7Layer up_ftp="20kbit" max_download="250kbit" down_others="10kbit" down_ssh="40kbit" down_http="100kbit" down_ftp="100kbit" ########## Upload ########## ip link set imq0 up tc qdisc add dev imq0 root handle 1: htb default 10 tc class add dev imq0 parent 1: classid 1:1 htb rate $max_upload ceil $max_upload tc class add dev imq0 parent 1:1 classid 1:10 htb rate $up_others ceil $max_upload tc class add dev imq0 parent 1:1 classid 1:20 htb rate $up_ssh ceil $max_upload tc class add dev imq0 parent 1:1 classid 1:30 htb rate $up_http ceil $max_upload tc class add dev imq0 parent 1:1 classid 1:40 htb rate $up_ftp ceil $max_upload tc filter add dev imq0 parent 1: protocol ip prio 10 handle 100 fw classid 1:10 tc filter add dev imq0 parent 1: protocol ip prio 1 handle 200 fw classid 1:20 tc filter add dev imq0 parent 1: protocol ip prio 1 handle 300 fw classid 1:30 tc filter add dev imq0 parent 1: protocol ip prio 1 handle 400 fw classid 1:40 tc qdisc add dev imq0 parent 1:10 handle 10:0 sfq perturb 10 tc qdisc add dev imq0 parent 1:20 handle 20:0 sfq perturb 10 tc qdisc add dev imq0 parent 1:30 handle 30:0 sfq perturb 10 tc qdisc add dev imq0 parent 1:40 handle 40:0 sfq perturb 10 iptables -t mangle -A POSTROUTING -m layer7 --l7proto ssh -j MARK --set-mark 200 iptables -t mangle -A POSTROUTING -m layer7 --l7proto http -j MARK --set-mark 300 iptables -t mangle -A POSTROUTING -m layer7 --l7proto ftp -j MARK --set-mark 400 iptables -t mangle -A POSTROUTING -o $ifc_zew -j IMQ --todev 0 ########## Download ########## ip link set imq1 up tc qdisc add dev imq1 root handle 1: htb default 10 tc class add dev imq1 parent 1: classid 1:1 htb rate $max_download ceil $max_download tc class add dev imq1 parent 1:1 classid 1:10 htb rate $down_others ceil $max_download tc class add dev imq1 parent 1:1 classid 1:20 htb rate $down_ssh ceil $max_download tc class add dev imq1 parent 1:1 classid 1:30 htb rate $down_http ceil $max_download tc class add dev imq1 parent 1:1 classid 1:40 htb rate $down_ftp ceil $max_download tc filter add dev imq1 parent 1: protocol ip prio 10 handle 100 fw classid 1:10 12 tc filter add dev imq1 parent 1: protocol ip prio 1 handle 200 fw classid 1:20 tc filter add dev imq1 parent 1: protocol ip prio 1 handle 300 fw classid 1:30 tc filter add dev imq1 parent 1: protocol ip prio 1 handle 400 fw classid 1:40 tc qdisc add dev imq1 parent 1:10 handle 10:0 sfq perturb 10 tc qdisc add dev imq1 parent 1:20 handle 20:0 sfq perturb 10 tc qdisc add dev imq1 parent 1:30 handle 30:0 sfq perturb 10 tc qdisc add dev imq1 parent 1:40 handle 40:0 sfq perturb 10 12 Layer7 rozpoznaje rodzaj pakietu oraz markuje go, dzięki czemu IMQ może wrzucić go do odpowiedniej kolejki.
10 Ograniczanie pasma internetowego za pomocą IMQ i 7Layer iptables -t mangle -A PREROUTING -m layer7 --l7proto ssh -j MARK --set-mark 200 13 iptables -t mangle -A PREROUTING -m layer7 --l7proto http -j MARK --set-mark 300 iptables -t mangle -A PREROUTING -m layer7 --l7proto ftp -j MARK --set-mark 400 iptables -t mangle -A PREROUTING -i $ifc_zew -j IMQ --todev 1 Skrypt ten różni się od poprzedniego jedną rzeczą. W przypadku dzielenia łącza według IP użyliśmy rozpoznawania źródłowego i docelowego IP za pomocą u32 match. Jak widać w tym przypadku z pomocą markowania pakietów przychodzi nam 7Layer. Bez tego narzędzia czynność ta stałaby się bardzo uciążliwa, a w niektórych przypadkach i niemożliwa. Duża ilość programów zawiera opcję zmiany portu transmisji lub w razie rosnącego przepływu danych zajmuje ich coraz więcej. Dlatego też rozpoznawanie pakietów po porcie źródłowym i docelowym nie wchodzi w grę. Składnia komendy iptables wykorzystującej 7Layer wygląda następująco: iptables [ustalenie tabeli] -m layer7 --l7proto [nazwa] -j [co dalej] Odpowiedni protokół musi oczywiście znajdować się w katalogu /etc/l7-protocols. Na przykład jeśli chcemy zablokować cały ruch sieci edonkey wpisujemy komendę: iptables -A FORWARD -m layer7 --l7proto edonkey -j DROP W /etc/l7-protocols znajduje się plik edonkey.pat. 7Layer to jedno z najlepszych narzędzi do wykrywania pakietów sieci p2p. 4 Zakończenie Podział pasma jest to stosunkowo prosta do wykonania i bardzo wygodna funkcja. Dzięki niej możemy zapobiec wszelkim nieporozumieniom związanym z dzieleniem łącza internetowego. Zazwyczaj stosowany jest zarówno podział ze względu na IP jak i na usługi. Najpierw dzielimy pasmo ze względu na IP (po równo), a potem każdy z użytkowników dzieli je na usługi według własnych potrzeb. Przy pomocy demona Cron można łatwo zmieniać podział pasma ze względu na porę dnia. Wystarczy napisać dwa skrypty - jeden działający w dzień, a drugi w nocy. Mam nadzieję, że teraz dzielenie Twojego łącza internetowego z sąsiadem będzie sprawniejsze i mniej kłopotliwe. Literatura [1] www.linuximq.net [2] l7-filter.sourceforge.net/howto 13 Markowanie pakietów przy użyciu 7Layer (tutaj ssh).