|
Strona 2 z 4
3. Dummenet
Dummynet to narzędzie pozwalające na kształtowanie ruchu
sieciowego w systemie FreeBSD. Aby móc z niego korzystać należy wkompilować w
jądro opcje:
options IPFW2
options DUMMYNET |
Dobrze jest też wkompilować opcje:
options DEVICE_POLLING
options HZ=1000 |
Pozwoli to zwiększyć interaktywność ruchu sieciowego
przepływającego przez dummynet. opcja HZ definiuje co ile ma następować jeden
"tik" zegara systemowego (w naszym przypadku 1000 oznacza co 1ms), który
odpowiada m.in. za szybkość przetwarzania regułek dummynetu (im większa
"częstotliwość" tego zegara tym więcej regułek na sekundę może przetworzyć
system a co za tym idzie pakiety mają mniejsze opóźnienia spowodowane przejściem
przez kod dummynetu). Z kolei opcja DEVICE_POLLING odpowiada za to (pokrótce),
że karta sieciowa obsługiwana jest troszeczkę szybciej.
Jeśli chcemy korzystać z dummynetu należy też w pliku "/etc/rc.conf"
dodać/zmienić opcje:
firewall_enable="YES"
firewall_rules="/etc/dummynet.rules"
|
gdzie "firewall_rules" określa nam nazwę pliku, z którego będą
wczytywane regułki dummynetu przy starcie systemu. W naszym przypadku będzie to
plik "/etc/dummynet.rules".
Należy też dodać do pliku "/etc/make.conf" (jeśli taki nie
istnieje należy go utworzyć) wpis "IPFW2=true".
W dummynecie rozróżniamy dwa rodzaje elementów sterujących
ruchem: rurki (pipes) i kolejki (queues).
3.1. Rurki
Rurka to kanał wydzielony z łącza posiadający zadaną
przepustowość. Odpowiada to mniej więcej rzeczywistej rurce w której płynie
woda. Do rurki trafia woda (pakiety) i płynie przez nią z określoną
przepustowością (średnica realnej rurki), której nie może przekroczyć. Jeśli do
rurki próbujemy "wlać" więcej pakietów niż jest to możliwe w związku ze
zdefiniowanym limitem przepustowości zostają one odrzucone.
Rurka posiada kilka parametrów, które możemy skonfigurować. Do interesujących
nas należą: przepustowość danej rurki (bw), wielkość bufora (queue - nie mylić z
kolejką) oraz opóźnienie (delay).
Przykładowo dodanie rurki, która z naszego łącza (podłączonego przez karę
sieciową "xl0") wydzieli kanał o przepustowości 128Kbit/s i buforze pakietów 6KB
wygląda tak:
ipfw add pipe 1 ip from any to any
via xl0
ipfw pipe 1 config bw 128Kbit/s queue 6Kbytes
Pierwsza linijka dodaje rurkę numer 1 (pipe 1) działającą na
pakiety w protokole ip (czyli tcp, udp i icmp) przechodzące z dowolnego adresu
(pierwsze "any") do dowolnego adresu (drugie "any") przez interfejs ("via") xl0.
Druga linijka konfiguruje nam parametry nowo dodanej rurki: przepustowość
128Kbitów/s oraz bufor (queue) 6KBajtów. Od chwili dodania tej rurki wszystkie
pakiety spełniające kryterium "z dowolnego adresu do dowolnego adresu" będą
mogły być przesyłane z maksymalną prędkością 128Kbitów/s.
Oprócz zdefiniowania dla rurki interfejsu, dla którego (na którym) ma działać
można też zdefiniować kierunek rurki. Kierunki mogą być dwa: przychodzący (in) i
wychodzący (out). Jest to szczególnie przydatne, jeśli mamy np. łącze
asynchroniczne typu ADSL i chcemy zdefiniować różne prędkości przesyłu dla ruchu
wychodzącego i wchodzącego. Przykładowo dla modemu ADSL o prędkości wychodzącej
128Kbitów/s i wchodzącej 512Kbitów/s będzie to wyglądało tak:
ipfw add pipe 1 ip from any to any
out via xl0
ipfw add pipe 2 ip from any to any in via xl0
ipfw pipe 1 config bw 128Kbit/s queue 6Kbytes
ipfw pipe 2 config bw 512Kbit/s queue 20Kbytes
Dodajemy rurke 1 (pipe 1) tylko dla ruchu wychodzącego (out)
przez interfejs xl0 oraz rurkę 2 (pipe 2) tylko dla ruchu wchodzącego przez
interfejs xl0. Obie te kolejki działają niezależnie. Dodatkowo możemy to zapisać
jeszcze inaczej jako:
ipfw add pipe 1 ip from any to any
out xmit xl0
ipfw add pipe 2 ip from any to any in recv xl0
ipfw pipe 1 config bw 128Kbit/s queue 6Kbytes
ipfw pipe 2 config bw 512Kbit/s queue 20Kbytes
Widać, że słówko "via" zostało zastąpione przez "xmit" dla
pakietów wychodzących (out) i "recv" dla pakietów przychodzących (in). Jest to
jeszcze dokładniejsza metoda zdefiniowania, jakich pakietów ma się tyczyć
konfiguracja rurki. Zalecam stosowanie zamiast ogólnej definicji przejścia przez
interfejs (np. "via xl0") bardziej precyzyjnej definiującej dodatkowo kierunek
("xmit xl0" lub "recv xl0").
Załóżmy, że mamy w sieci trzy komputery. Nasze łącze to
wspomniany wyżej ADSL 128/512Kbit/s. Teraz chcemy aby każdy z tych trzech
komputerów miał przydzielone "na sztywno" pasmo, którego nie może przekroczyć. W
naszym przypadku niech komputer numer 1 o adresie 192.168.0.2 będzie bardziej
uprzywilejowany i dostanie polowe całkowitej dostępnej przepustowości (czyli
64Kbit/s dla danych przez niego wysyłanych i 256Kbit/s dla danych odbieranych) a
pozostałe dwa komputery dostaną resztę dostępnej przepustowości podzielonej po
równo (czyli każdy z nich 32Kbit/s dla danych wysyłanych i 128 dla danych
odbieranych). Konfiguracja powinna wyglądać tak:
ipfw add pipe 1 ip from 192.168.0.2
to any out xmit xl0
ipfw add pipe 2 ip from any to 192.168.0.2 in recv xl0
ipfw pipe 1 config bw 64Kbit/s queue 4Kbytes
ipfw pipe 2 config bw 256Kbit/s queue 8Kbytes
ipfw add pipe 3 ip from 192.168.0.3
to any out xmit xl0
ipfw add pipe 4 ip from any to 192.168.0.3 in recv xl0
ipfw pipe 3 config bw 32Kbit/s queue 2Kbytes
ipfw pipe 4 config bw 128Kbit/s queue 6Kbytes
ipfw add pipe 5 ip from 192.168.0.4
to any out xmit xl0
ipfw add pipe 6 ip from any to 192.168.0.4 in recv xl0
ipfw pipe 5 config bw 32Kbit/s queue 2Kbytes
ipfw pipe 6 config bw 128Kbit/s queue 6Kbytes
Widać, że dla każdego komputera musimy dodać po dwie rurki -
jedną na ruch wychodzący a drugą na wchodzący (w sumie aż 6 rurek), ponieważ
taka jest specyfika łącza ADSL. Widać też, że zmieniły się adresy, dla których
rurki mają działać. W pierwszej i drugiej rurce "any" zostało zastąpione przez
"192.168.0.2". Oznacza to, że rurka 1 ma działać tylko dla pakietów wychodzących
z adresu 192.168.0.2 do dowolnego innego adresu przez interfejs xl0 a rurka 2 ma
działać dla pakietów przychodzących dla adresu 192.168.0.2 z dowolnego innego
adresu przez interfejs xl0.
3.2. Kolejki
Przejdźmy teraz do drugiego rodzaju elementu sterującego
ruchem, czyli do kolejek. W przeciwieństwie do rurek kolejki nie są
zdefiniowanym na sztywno limitem przepustowości, lecz są bezpośrednio związane
ze zdefiniowanymi przez nas rurkami. W takim przypadku do rurki trafiają pakiety
z odpowiednimi, zdefiniowanymi przez nas w konfiguracji kolejek wagami (weight)
i w zależności od wag ją opuszczają. Oznacza to, że im wyższą wagę ma dana
kolejka tym jest ona bardziej uprzywilejowana. Przykładowo zdefiniujmy dwie
kolejki dla dwóch różnych komputerów w sieci. Pierwszy ma mieć trzy razy wyższy
"priorytet" niż drugi. Najpierw musimy zdefiniować rurki, jako element, do
których "podczepimy" kolejki:
ipfw pipe 1 config bw 128Kbit/s
queue 6Kbytes
ipfw pipe 2 config bw 512Kbit/s queue 20Kbytes
Od razu widać, że nie definiujemy tu jak poprzednio na jakie
pakiety mają działać rurki lecz ustawiamy tylko ich konfiguracje. Kierowaniem
odpowiednich pakietów do rurek zajmą się kolejki. Dodajmy je zgodnie z
wcześniejszymi założeniami:
ipfw add queue 1 ip from 192.168.0.2
to any out xmit xl0
ipfw add queue 2 ip from any to 192.168.0.2 in recv xl0
ipfw queue 1 config pipe 1 weight 3 queue 2Kbytes
ipfw queue 2 config pipe 2 weight 3 queue 6Kbytes
ipfw add queue 3 ip from 192.168.0.3
to any out xmit xl0
ipfw add queue 4 ip from any to 192.168.0.3 in recv xl0
ipfw queue 3 config pipe 1 weight 1 queue 2Kbytes
ipfw queue 4 config pipe 2 weight 1 queue 6Kbytes
Widać, że tak jak poprzednio w przypadku rurek dodajemy po
dwie kolejki dla każdego adresu w sieci lokalnej. Pierwsza kolejka określona
jest dla pakietów wychodzących z danego adresu a druga dla pakietów
przychodzących do niego. Zajmijmy się dokładniej omówieniem konfiguracji kolejki
na przykładzie kolejek dla adresu 192.168.0.2. Najpierw dodajemy kolejkę 1 dla
pakietów wychodzących od niego przez interfejs xl0 a następnie kolejkę 2 dla
pakietów przychodzących dla niego przez tenże interfejs. Następnie określamy
konfiguracje kolejki 1 jako powiązanej z rurką 1 (pipe 1), czyli rurką
wszystkich pakietów wychodzących z naszej bramy, posiadającą wagę 3 (weight 3)
oraz bufor 2Kbajty. Analogicznie w przypadku kolejki 2 wiążemy ją z rurką 2 (pipe
2), ustawiamy taką samą wagę jak dla kolejki wychodzącej (weight 3) oraz większy
bufor 6Kbytes. W przypadki kolejek 3 i 4 wagi wynoszą 1. Warto teraz uświadomić
sobie jak działają wagi. Posłużmy się do tego powyższym przykładem. Otóż jeśli
kolejki 1 i 2 mają wagę 3 a kolejki 3 i 4 wagę 1 to znaczy to, że przy
maksymalnym obciążeniu łącza na 4 pakiety wychodzące przez nasz interfejs xl0
trzy z nich będą należały do komputera 192.168.0.2 a jeden do komputera
192.168.0.3. To samo tyczy się pakietów przychodzących - trzy z nich będą dla
komputera 192.168.0.2 a jeden dla 192.168.0.3. Widać już zatem w jaki sposób
wyliczane są "priorytety" pakietów - "priorytet" dla komputera 192.168.0.2 to
waga jego kolejki w danym kierunku (np. 3 dla kierunku wychodzącego) podzielona
przez całkowitą sumę wszystkich wag w danym kierunku (3 + 1 = 4). Oczywiście
zaletą wag jest też to, że nawet przy ustawieniu drastycznie różnych wag (np.
waga 99 dla 192.168.0.2 i waga 1 dla 192.168.0.3) kolejka z mniejszą wagą nie
będzie mogła być "zagłodzona". Po prostu komputer, którego pakiety trafiają w
kolejkę o tak niskiej wadze będzie znacznie wolniej komunikował się z
Internetem. Warto też wspomnieć, że wagi mogą mieć wartości z przedziału 1-100
(ale ze względu na sposób obliczania "priorytetów" nie zaleca się stosowanie tak
wysokich wag; wystarczające są wagi z zakresu 1-10).
Dodatkową zaletą kolejek jest też to, że o ile w rurkach wprowadzaliśmy "na
sztywno" limit, którego nie można było przekroczyć tu jest on dynamiczny.
Przykładowo jeśli mamy dwa komputery w sieci i zdefiniujemy im limity za pomocą
rurek, to w momencie kiedy drugi komputer jest wyłączony (czyli nie korzysta z
sieci) ten pierwszy może mieć tylko przydzielony przez nas transfer mimo, że
pula transferu komputera drugiego jest niewykorzystana (czyli można powiedzieć,
że "przepustowość się marnuje"). W przypadku kolejek jeśli drugi komputer jest
wyłączony pierwszy ma dostępną całą przepustowość łącza a w momencie ponownego
włączenia komputera drugiego podział odbywa się znów zgodnie ze zdefiniowanymi
wagami.
W przykładzie powyżej mieliśmy tylko dwa komputery. Ale co zrobić, jeśli mamy
ich więcej (np. 200)? Możemy zdefiniować 400 kolejek (200 na pakiety wchodzące i
200 na wychodzące) ręcznie albo posłużyć się tzw. kolejkami dynamicznymi.
Kolejki dynamiczne to takie kolejki, które są automatycznie tworzone przez
dummynet wtedy, kiedy są potrzebne (kiedy np. dany komputer komunikuje się z
Internetem) a potem usuwane. Aby zdefiniować kolejki dynamiczne należy dodać do
konfiguracji normalnych kolejek parametr maski (mask). Zobaczmy to na
przykładzie - powiedzmy, że mamy zrobić kolejki dynamiczne dla sieci z maską
24-o bitową. Wyglądałoby to mniej więcej tak (znów łącze ADSL):
ipfw add queue 1 ip from
192.168.0.0/24 to any out xmit xl0
ipfw add queue 2 ip from any to 192.168.0.0/24 in recv xl0
ipfw queue 1 config pipe 1 weight 2 queue 4Kbytes mask src-ip
0x000000ff
ipfw queue 2 config pipe 2 weight 2 queue 8Kbytes mask dst-ip
0x000000ff
Taka konfiguracja oznacza, że dla każdego komputera z sieci
lokalnej (192.168.0.0/24), który chce przesyłać dane tworzona jest automatycznie
kolejka o wadze 2 (weight 2) jeśli odpowiednio adres źródłowy pakietu z danego
komputera (mask src-ip 0x000000ff) lub jego adres docelowy (mask dst-ip
0x000000ff) spełnia kryterium maski 24-bitowej (0x000000ff oznacza właśnie maskę
24-o bitową w zapisie heksadecymalnym).
Poznaliśmy już w jaki sposób stosować podział "na twardo"
(czyli rurki) oraz "na miękko" (czyli kolejki) oraz jak zrobić podział
dynamiczny za pomocą kolejek. Spróbujmy tą wiedzę wykorzystać do stworzenia
bardziej rozbudowanej konfiguracji. Załóżmy, że mamy sieć dziesięciu komputerów,
w której są różni użytkownicy. Jedni lubią pograć w Quake'a, inni dużo ściągają
przez programy p2p a jeszcze inni potrzebują do szczęścia tylko aby mogli
bezproblemowo przeglądać strony WWW i korzystać z komunikatorów internetowych.
Wprowadzimy też do tejże konfiguracji kolejki dynamiczne aby każdy z
użytkowników korzystających z sieci dostawał swoją własną kolejkę dla danego
rodzaju ruchu. W jaki sposób pogodzić potrzeby każdej z tych grup? Z pomocą
przychodzi oczywiście dummynet. Spróbujmy stworzyć konfiguracje dla takiej sieci
kierując się następującymi założeniami:
Łącze na świat do ADSL 128/512Kbit
Każdy użytkownik dostaje swoją własną kolejkę dla określonego rodzaju ruchu
Cokolwiek się dzieje w sieci gracze sieciowi powinni mieć niski "ping" (w ich
przypadku to tzw. UDP ping)
Nic nie powinno zakłócać przeglądania stron WWW ani funkcjonowania poczty
Użytkowników korzystających z p2p spychamy na margines
Teraz spróbujmy zapisać to w języku dummynetu. Najpierw
definiujemy rurki dla modemu ADSL:
ipfw pipe 1 config bw 128Kbit/s
queue 8Kbytes
ipfw pipe 2 config bw 512Kbit/s queue 24Kbytes
Następnie podczepiamy pod rurki kolejki dynamiczne dla
poszczególnych rodzajów ruchu.
Dla gier sieciowych po UDP oraz rozwiązywania nazw DNS:
ipfw add queue 1 udp from
192.168.0.0/24 to any out xmit xl0
ipfw add queue 2 udp from any to 192.168.0.0/24 in recv xl0
ipfw queue 1 config pipe 1 weight 5 queue 4Kbytes mask src-ip
0x000000ff
ipfw queue 2 config pipe 2 weight 5 queue 8Kbytes mask dst-ip
0x000000ff
Dla stron WWW oraz poczty (porty: WWW - 80, POP3 - 110, SMTP -
25):
ipfw add queue 3 tcp from
192.168.0.0/24 to any 80,25,110 out xmit xl0
ipfw add queue 4 tcp from any 80,25,110 to 192.168.0.0/24 in
recv xl0
ipfw queue 1 config pipe 1 weight 3 queue 8Kbytes mask src-ip
0x000000ff
ipfw queue 2 config pipe 2 weight 3 queue 12Kbytes mask
dst-ip 0x000000ff
I wreszcie dla całej reszty (czyli w tym programów p2p):
ipfw add queue 5 ip from
192.168.0.0/24 to any out xmit xl0
ipfw queue 1 config pipe 1 weight 1 queue 4Kbytes mask src-ip
0x000000ff
ipfw queue 2 config pipe 2 weight 1 queue 8Kbytes mask dst-ip
0x000000ff
|