{ }
menu zespół linki Logowanie
Stabilny hosting
BSDGuru zawdzięcza
firmie Datanet.pl
Hosting BSDGuru.org - DataNet.pl

Praktyczne IPFW

Spis treści:

0. Tytułem wstępu

1. Komendy ipfw

2. Firewall

3. Dummenet

3.1. Rurki
3.2. Kolejki

4. Dwa w jednym

5. Dodatek

5.1. NATowe niuanse

5.2. Obliczanie rozmiaru bufora (queue) dla rurki
lub kolejki


6. Na zakończenie

0. Tytułem wstępu

Na potrzeby tego tekstu zakładam, że Czytelnik dysponuje łączem ADSL 128/512Kbit/s oraz systemem FreeBSD 4.9-STABLE oraz podstawami wiedzy z zakresu sieci opartych o protokół TCP/IP. Ma także poprawnie skonfigurowany do pracy sieciowej system.

1. Komendy ipfw

Zanim dowiesz się jak pisać regułki dobrze jest zapoznać się z poleceniami
obsługującymi działanie pakietu ipfw. Najważniejsze to:

ipfw zero - zeruje liczniki dla wszystkich regułek (każda
regułka podczas działania zlicza pasującą do niej ilość pakietów i ich sumaryczny
rozmiar w bajtach).

ipfw flush - czyści (kasuje) wszystkie regułki oprócz domyślnych.
Przed każdą większą zmianą regułek (np. za pomocą skryptu) dobrze jest wyczyścić
regułki aby nowe nie nakładały się na stare.

ipfw show - pokazuje wszystkie aktualnie działające regułki
oraz ich statystyki (ilość pakietów i danych pasujących do tej regułki).

ipfw show <numer> - pokazuje tylko regułkę numer "<numer>"
(np. ipfw show 100).

2. Firewall

Pakiet ipfw został stworzony na potrzeby ścian ogniowych (firewalls). Przyjrzyjmy
mu się zatem bliżej.

Aby móc korzystać z funkcjonalności firewall opartego o ipfw należy wkompilować
w jądro opcje:

options IPFW2

Należy też w pliku "/etc/rc.conf" dodać/zmienić opcje:

firewall_enable=&quot;YES&quot;<br>

firewall_rules=&quot;/etc/firewall.rules&quot;

gdzie "firewall_rules" określa nam nazwę pliku, z którego będą wczytywane
regułki firewalla przy starcie systemu. W naszym przypadku będzie to plik "/etc/firewall.rules".

Należy też do pliku "/etc/make.conf" dodać:

IPFW2=true

Składnia poleceń ipfw jest bardzo zbliżona do naturalnego języka angielskiego (co będzie można zaobserwować w przykładach poniżej), dzięki czemu pisanie regułek za jego pomocą jest bardzo proste. W ipfw występują trzy interesujące nas rodzaje akcji związanych z regułką: regułke można dodać (add), zmienić (modify) oraz usunąć (delete). Każda z regułek w momencie dodania jej do konfiguracji dostaje swój własny "numer identyfikacyjny" (np. regułka może mieć numer 100), dzięki czemu możemy się do niej później w prosty sposób odwoływać. Jeśli w konfiguracji danej regułki nie podamy jej numerka jest on przypisywany automatycznie na zasadzie "dostajesz pierwszy wolny".
Następnie po zdefiniowaniu akcji dodania lub modyfikacji (przy kasowaniu regułki nie podajemy żadnego parametru poza numerem, czyli np. "ipfw delete 100" skasuje nam regułkę numer 100 bez zaglądania, co w niej się znajduje) należy zdefiniować parametry danej regułki. Mogą to być: rodzaj akcji jaka ma być podjęta (zezwalaj - allow, odrzóć - deny, przekieruj - fwd, zliczaj - count, odrzuć z informacją dla nadawcy - reject oraz reset) protokół dla jakiego ma działać regułka (np. ip, tcp, udp, icmp), adres źródłowy pakietu, adres docelowy pakietu, kierunek przepływu pakietu ("in" lub "out"), interfejs oraz kilka innych które poznamy za chwile.

Przykładowa regułka o numerze 100, która pozwala na przepuszczenie dowolnego ruchu (czyli z dowolnego adresu do dowolnego adresu) w protokole ip (czyli zarówno tcp jak i udp czy icmp) przez interfejs xl0 wyglądać może tak:

ipfw add 100 allow ip from any to any via xl0

Jeśli natomiast przykładowo chcemy, żeby wszystkie pakiety przychodzące do nas w protokole tcp na port 80 były odrzucane ale reszta była akceptowana musimy dodać już dwie regułki:

ipfw add 100 deny tcp from any to me 80 via xl0<br> ipfw add 200 allow ip from any to any via xl0

Co się dzieje po dodaniu powyższego zapisu? Załóżmy, że do naszego komputera na port 21 przez interfejs xl0 przychodzi pakiet z adresu 111.222.333.444 z portu 1025. Trafia do regułek naszego firewalla i jest sprawdzany. Najpierw sprawdzany jest przez regułkę 100, czyli: jeśli pakiet jest pakietem tcp (nasz jest, więc regułka sprawdza go dalej) i pochodzi z dowolnego adresu (111.222.333.444 to jakby nie patrzeć dowolny adres) z dowolnego portu (1025 to też dowolny port) a przychodzi na mój adres ("me" oznacza wszystkie możliwe adresy ip przypisane do mojego komputera na wszystkich interfejsach; nasz pakiet to spełnia, bo przyszedł przez interfejs xl0) na port 80 (pakiet jest skierowany na port 21, czyli nie spełnia tego kryterium). W związku z niespełnieniem kryterium portu docelowego (80) regułka stwierdza, że pakiet nie spełnia jej kryteriów, więc kieruje go do następnej w kolejności regułki, czyli 200. W regułce 200 sprawdzane jest: jeśli pakiet jest w protokole ip (tcp zalicza się do ip, więc jest to spełnione) i pochodzi z dowolnego adresu z dowolnego portu a zaadresowany jest do dowolnego przypisanego do mojego komputera adresu i przeszedł przez interfejs xl0 to przepuść go. Jest to spełnione, czyli pakiet jest przepuszczany i opuszcza kod firewalla (poza osobnym przypadkiem, kiedy tak nie jest - patrz "Dwa w jednym").

Jak widać zapis regułek jest bardzo wygodny i intuicyjny. Widać też, że zgodnie z powyższym przykładem pakiet jest sprawdzany po kolei z każdą regułką aż trafi na pierwszą, do której pasuje. Wtedy podejmowana jest związana z tą regułką akcja i pakiet opuszcza ipfw.
Oprócz zaprezentowanych akcji, czyli "allow", która przepuszcza pakiet oraz "deny", która "wyrzuca" pakiet bez jakiejkolwiek informacji zwrotnej dla nadawcy istnieją też (wspomniane na początku) akcje "reject" oraz "reset". Akcja "reject" powoduje, że pakiet zostaje odrzucony a do nadawcy odsyłana jest informacja "host unreachable", czyli wygląda to dla nadawcy tak, jakby nasz komputer był wyłączony (dla danego pakietu). Akcja "reset" powoduje odrzucenie pakietu i wysłanie do nadawcy pakietu tcp "RST". Do czego to może się nam przydać? Otóż zobaczmy to na przykładzie portu 113 (ident), który jest często przepytywany przez np. serwery FTP podczas łączenia się z nimi. Kiedy użyjemy dla tego portu akcji "drop" pakiet z serwera FTP przyjdzie i zostanie skasowany ale serwer FTP nie dostanie żadnej informacji o tym, więc będzie czekał, aż nasz komputer wyśle mu odpowiedz na jego pakiet. Taka odpowiedz nigdy nie nadejdzie, więc serwer FTP będzie czekał aż upłynie czas przewidziany na powrót informacji zwrotnej i dopiero wtedy zakłada, że nasz port 113 jest zamknięty. Nie jest to zbyt dobre, gdyż powoduje wydłużenie czasu łączenia się z serwerem (nawet o kilka sekund). Rozwiązaniem jest wysłanie informacji zwrotnej do serwera FTP, że nasz port 113 jest zamknięty. Nie będzie on wtedy niepotrzebnie czekał i od razu przejdzie do negocjacji już konkretnie połączenia ftp. Regułka ipfw, która wysyła pakiet RST jako informacje zwrotną do serwera FTP może mieć postać:

ipfw add reset tcp from any to me 113

Ciekawym parametrem regułki jest też "limit". Pozwala on na limitowanie ilości połączeń o takich parametrach jak zdefiniowane w regułce (zachowanie to jest zachowaniem związanym z tzw. regułkami dynamicznymi, o czym niżej). Przykładowo regułka pozwalająca na nawiązanie maksymalnie 10 połączeń na port 80 z naszej sieci (np. 192.168.0.0/24) może wyglądać tak:

ipfw add allow tcp from 192.168.0.0/24 to any 80 limit src-addr 10

Parametr "src-addr" określa, że limit ma się tyczyć połączeń nawiązanych z naszej sieci. Istnieje też możliwość zdefiniowania limitu odwrotnego, czyli limitu połączeń na dany adres w sieci 192.168.0.0/24 ("dst-addr"), przykładowo:

ipfw add allow tcp from any to 192.168.0.0/24 80 limit dst-addr 10

oraz zdefiniowanie limitu na poszczególny port ("src-port" oraz "dst-port").

Wspomnieliśmy o zachowaniu związanym z regułkami dynamicznymi. Spróbujmy się temu przyjrzeć bliżej. Specyfika protokołu TCP/IP jest taka, że po nawiązaniu połączenia zmienia się jego status. Przykładowo załóżmy, że łączymy się z serwerem WWW. Zakładamy, że nasze regułki ipfw pozwalają (teoretycznie jak się zaraz okaże) tylko na komunikację z serwerami WWW:

ipfw add allow tcp from me to any 80<br>
  ipfw add deny ip from any to any

Nasz komputer bez problemu wysyła pakiet nawiązujący połączenie na port 80 zdalnego serwera (pierwsza regułka pasuje) ale już potwierdzenie ze strony owego serwera zostanie skasowane (nie pasuje do pierwszej regułki, bo przychodzi z adresu "any" z portu 80 na adres mojego komputera, czyli w odwrotną stronę niż zakłada regułka), czyli połączenie nie zostanie nawiązane. Żeby uniknąć takiego zachowania wprowadza się właśnie regułki dynamiczne, czyli regułki, które są generowane na podstawie innych. W naszym przykładzie aby transmisja mogła mieć miejsce należy zmienić zapis regułek na:

ipfw add allow tcp from me to any 80 setup keep-state<br>
  ipfw add deny ip from any to any

Będzie to oznaczało, że ipfw ma dla każdego połączenia nawiązywanego ze zdalnym serwerem WWW na port 80 (setup - połączenie nawiązywane) utworzyć regułke na podstawie oryginalnej regułki (keep-state), która będzie wykonywała oryginalną akcję (u nas "allow"). Czyli zapisanie tylko jednostronnej regułki (bo tylko dla pakietów wychodzących od nas) umożliwia dwustronną transmisję. Całą "logiką" takiego połączenia zajmie się sam ipfw.
Dodatkowo w przypadku regułek dynamicznych możemy definiować połączenie za pomocą jego aktualnego stanu. Przykładowo zapis:

ipfw add 100 check-state<br>

  ipfw add 200 allow tcp from me to any setup<br>
  ipfw add 300 allow tcp from any to any established<br>
  ipfw add 400 deny ip from any to any

powoduje, że na samym początku pakiet jest sprawdzany pod względem stanu w jakim się znajduje (istnieją trzy możliwe stany - pakiet może być "nowy", czyli nawiązujący połączenie, "powiązany" z danym połączeniem np. w przypadku FTP oraz "nawiązany", czyli powstały w następstwie poprawnego wynegocjowania połączenia przez pakiet "nowy") za pomocą komendy "check-state" a następnie ipfw posiadając informacje o stanie pakietu może go dopasowywać dalej. U nas regułka 200 pozwala na wysyłanie pakietów nawiązujących połączenie (pakiet "nowy") a regułka 300 pozwala na ruch pakietów należących do już nawiązanych połączeń bez ograniczeń. Warto zaznaczyć, że sprawdzanie stanu możliwe jest tylko dla pakietów tcp oraz udp a nie dla icmp.

Posiadając wiedzę o budowie regułek tak statycznych jak i dynamicznych spróbujmy skonstruować prosty firewall do ochrony stacji roboczej pracującej w sieci o adresie 192.168.0.0/24. Stacja taka nie udostępnia żadnych usług sieciowych na zewnątrz poza SAMBą (pracuje w sieci Microsoft Network i jest jej to potrzebne do komunikacji z innymi komputerami w sieci). Przykładowa konfiguracja może wyglądać tak:

ipfw add 100 allow tcp from 192.168.0.0/24 137,138,139 to me setup keep-state<br>
  ipfw add 200 allow udp from 192.168.0.0/24 137,138,139 to me setup keep-state<br>
  ipfw add 300 allow tcp from me to any setup keep-state<br>
  ipfw add 400 allow ucp from me to any setup keep-state<br>
  ipfw add 500 allow icmp from any to any<br>
  ipfw add 600 deny ip from any to any

Po kolei: regułki 100 i 200 oznaczają, że inne komputery mogą łączyć się z naszym za pomocą protokołu tcp oraz udp z portów 137,138,139 (porty używane przez Microsoft Network oraz Sambę) na dowolny port i w efekcie tych połączeń powstaną regułki dynamiczne. Regułki 300 i 400 pozwalają naszemu komputerowi łączyć się z dowolnym adresem na protokole tcp oraz udp bez ograniczeń. Regułka 500 pozwala na dowolną transmisję protokołem icmp. Regułka 600 kasuje wszystkie pakiety, które nie pasowały do żadnej z powyższych regułek (czyli powinny się do niej kwalifikować pakiety przychodzące do nas ale nie powiązane z żadnym nawiązanym przez nas połączeniem).

Spróbujmy też stworzyć zestaw regułek dla komputera pełniącego role prostej bramy internetowej. Komputer taki w przeciwieństwie do naszej stacji roboczej nie powinien przepuszczać transmisji z/do sieci Microsoft Network ale z kolei ma uruchomioną na porcie 22 usługę serwera SSH, która powinna być dostępna zarówno z sieci lokalnej jak i z Internetu. Komputer ten ma dwa interfejsy sieciowe: xl0 podłączony do modemu ADSL (adres 81.21.31.41) oraz xl1 do sieci LAN (sieć 192.168.0.0/24). Przykładowa konfiguracja może to wyglądać tak:

ipfw add 100 check-state<br>
  ipfw add 200 allow ip from any to any via xl1<br>
  ipfw add 300 reset tcp from any to 192.168.0.0/24,81.21.31.41 113 in recv xl0<br>
  ipfw add 400 deny tcp from any to any 137,138,139,445<br>
  ipfw add 500 deny udp from any to any 137,138,139<br>

  ipfw add 600 allow tcp from any to 192.168.0.0/24,81.21.31.41 via xl0 established<br>
  ipfw add 700 allow udp from any to 192.168.0.0/24,81.21.31.41 via xl0 established<br>
  ipfw add 800 allow tcp from 192.168.0.0/24 to any via xl0 setup<br>
  ipfw add 900 allow udp from 192.168.0.0/24 to any via xl0 setup<br>
  ipfw add 1000 allow icmp from any to any<br>
  ipfw add 1100 allow tcp from any to 81.21.31.41 22 via xl0 setup<br>

  ipfw add 65000 deny ip from any to any

W powyższych regułkach widzimy, że jako adres źródłowy lub docelowy możemy
wpisać pojedynczy adres, adres całej sieci lub kilka adresów oddzielonych przecinkami.

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<br>
  options DUMMYNET

Dobrze jest też wkompilować opcje:

options DEVICE_POLLING<br>

  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=&quot;YES&quot;<br>

  firewall_rules=&quot;/etc/dummynet.rules&quot;

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<br>
  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<br>
  ipfw add pipe 2 ip from any to any in via xl0<br>
  ipfw pipe 1 config bw 128Kbit/s queue 6Kbytes<br>
  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<br>
  ipfw add pipe 2 ip from any to any in recv xl0<br>

  ipfw pipe 1 config bw 128Kbit/s queue 6Kbytes<br>
  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<br>
  ipfw add pipe 2 ip from any to 192.168.0.2 in recv xl0<br>
  ipfw pipe 1 config bw 64Kbit/s queue 4Kbytes<br>
  ipfw pipe 2 config bw 256Kbit/s queue 8Kbytes

ipfw add pipe 3 ip from 192.168.0.3 to any out xmit xl0<br>

  ipfw add pipe 4 ip from any to 192.168.0.3 in recv xl0<br>
  ipfw pipe 3 config bw 32Kbit/s queue 2Kbytes<br>
  ipfw pipe 4 config bw 128Kbit/s queue 6Kbytes

ipfw add pipe 5 ip from 192.168.0.4 to any out xmit xl0<br>
  ipfw add pipe 6 ip from any to 192.168.0.4 in recv xl0<br>
  ipfw pipe 5 config bw 32Kbit/s queue 2Kbytes<br>

  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<br>
  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<br>
  ipfw add queue 2 ip from any to 192.168.0.2 in recv xl0<br>
  ipfw queue 1 config pipe 1 weight 3 queue 2Kbytes<br>
  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<br>
  ipfw add queue 4 ip from any to 192.168.0.3 in recv xl0<br>
  ipfw queue 3 config pipe 1 weight 1 queue 2Kbytes<br>
  ipfw queue 4 config pipe 2 weight 1 queue 6Kbytes

Widać, że tak jak poprzedni 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<br>
  ipfw add queue 2 ip from any to 192.168.0.0/24 in recv xl0<br>

  ipfw queue 1 config pipe 1 weight 2 queue 4Kbytes mask src-ip 0x000000ff<br>
  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:

  1. Łącze na świat do ADSL 128/512Kbit
  2. Każdy użytkownik dostaje swoją własną kolejkę dla określonego rodzaju ruchu
  3. Cokolwiek się dzieje w sieci gracze sieciowi powinni mieć niski "ping"
    (w ich przypadku to tzw. UDP ping)
  4. Nic nie powinno zakłócać przeglądania stron WWW ani funkcjonowania poczty
  5. 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<br>
  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<br>
  ipfw add queue 2 udp from any to 192.168.0.0/24 in recv xl0<br>
  ipfw queue 1 config pipe 1 weight 5 queue 4Kbytes mask src-ip 0x000000ff<br>
  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<br>
  ipfw add queue 4 tcp from any 80,25,110 to 192.168.0.0/24 in recv xl0<br>

  ipfw queue 1 config pipe 1 weight 3 queue 8Kbytes mask src-ip 0x000000ff<br>
  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<br>
  ipfw add queue 6 ip from any to 192.168.0.0/24 in recv xl0<br>
  ipfw queue 1 config pipe 1 weight 1 queue 4Kbytes mask src-ip 0x000000ff<br>
  ipfw queue 2 config pipe 2 weight 1 queue 8Kbytes mask dst-ip 0x000000ff

4. Dwa w jednym

Poznaliśmy zasady tworzenia firewalla w oparciu o ipfw oraz zasady tworzenia regułek dummynetu. A jeśli będziemy chcieli stworzyć jeden zestaw regułek, który będzie pełnił jednocześnie role firewalla i kontrolował ruch sieciowy? Możemy to zrobić łącząc ze sobą firewalla opartego na ipfw i regułki dummynetu w jednym pliku (skrypcie). Zanim przystąpimy do budowy takiej hybrydy musimy zdać sobie sprawę z pewnej niedogodności. Otóż ipfw przetwarza pakiet aż do trafienia na taką regułkę, do której ów pasuje. Ta sama funkcjonalność tyczy się dummynetu. Jeśli mamy dwie następujące po sobie kolejki to zadziała tylko pierwsza pasująca. Następna już nie będzie przetwarzana. Jakie ma to konsekwencje? Otóż jeśli w jednym skrypcie połączymy funkcjonalność ipfw jako firewalla oraz dummynetu to może się okazać (w zależności od kolejności regułek), że po przejściu przez odpowiednią regułkę dummynetu pakiet nie będzie już przetwarzany przez regułki firewalla lub odwrotnie - po trafieniu na pasującą regułkę firewalla nie będzie już brany pod uwagę przez dummynet. Aby zmienić to zachowanie należy w pliku "/etc/sysctl.conf" ustawić zmienną "net.inet.ip.fw.one_pass=0" (domyślnie ma ona wartość 1). Spowoduje to to, że pakiet po przejściu przez regułki dummynetu (który powinien być przetwarzany pierwszy) powróci do ipfw (z już przydzieloną dla ruchu rurką lub kolejką) i będzie mógł być dodatkowo przetwarzany przez regułki firewalla. Czyli uzyskujemy firewall z kontrolą przepustowości!
Przykładowa konfiguracja dla stacji roboczej (w oparciu o poprzednio stworzony dla niej firewall) może wyglądać tak (zakładamy, że sieć LAN ma przepustowość 100Mbit/s a interfejs do tej sieci to xl0):

ipfw pipe 1 config bw 100Mbit/s queue 50Kbytes

ipfw add 100 queue 1 tcp from me to any out xmit xl0 iplen 0-80 tcpflags
  ack<br>

  ipfw queue 1 config pipe 1 weight 7 queue 4<br>
  ipfw add 200 skipto 1000 tcp from me to any out xmit xl0 iplen 0-80 tcpflags
  ack

ipfw add 300 queue 2 tcp from any to me in recv xl0 iplen 0-80 tcpflags
  ack<br>
  ipfw queue 2 config pipe 1 weight 7 queue 4<br>
  ipfw add 400 skipto 1000 tcp from any to me in recv xl0 iplen 0-80 tcpflags
  ack

ipfw add 500 queue 3 ip from 192.168.0.0/24 to me out xmit xl0<br>
  ipfw queue 3 config pipe 1 weight 1 queue 16Kbytes<br>

  ipfw add 600 queue 4 ip from any to 192.168.0.0/24 in recv xl0<br>
  ipfw queue 4 config pipe 1 weight 1 queue 16Kbytes

ipfw add 1000 allow tcp from 192.168.0.0/24 137,138,139 to me setup keep-state<br>
  ipfw add 1100 allow udp from 192.168.0.0/24 137,138,139 to me setup keep-state<br>
  ipfw add 1200 allow tcp from me to any setup keep-state<br>
  ipfw add 13000 allow ucp from me to any setup keep-state<br>

  ipfw add 1400 allow icmp from any to any<br>
  ipfw add 65000 deny ip from any to any

Teraz przeanalizujmy co robi powyższa konfiguracja. Pakiet wpada do ipfw. Najpierw przetwarzany jest przez dummynet i może być pakietem o wielkości 0-40 bajtów z ustawioną flagą ACK, wtedy trafia do kolejki 1 lub 2 (w zależności od kierunku jego przepływu) a następnie wędruje z powrotem do kodu firewalla. Następna w kolejności regułka to "skipto" (pozwalająca na "przeskok" do regułki o podanym po "skipto" numerze), która przerzuca pakiet do regułki 1000, czyli już do kodu właściwego firewalla (jak ów działa zostało opisane w sekcji dotyczącej ipfw). Jeśli nasz pakiet nie jest pakietem o wielkości 0-40 bajtów i ustawionej fladze ACK to zostaje przechwycony przez kolejkę 3 lub 4 (znów w zależności od kierunku przepływu) a później trafia do kodu firewalla (kolejki 3 i 4 znajdują się bezpośrednio nad regułkami firewalla, więc tutaj "skipto" nie jest już potrzebne).

Zapewne zastanawiać się możesz, po co wprowadzać kolejkowanie pakietów na stacji roboczej? Otóż taka konfiguracja, która nadaje małym pakietom potwierdzającym (z flagą ACK) wyższy "priorytet" niż reszcie transmisji pozwala na dobrą interaktywność przy bardzo obciążonej karcie sieciowej (transmisja na granicy maksymalnej przepustowości medium). Jeśli często np. ściągamy coś od kolegów z sieci LAN a w tym samym czasie przeglądamy strony WWW może to mieć bardzo pozytywny wpływ na komfort naszego korzystania z sieci.

Teraz spróbujmy skonfigurować firewall na naszej bramie z podziałem za pomocą dynamicznych kolejek opisanej w sekcji dummynetu:

ipfw pipe 1 config bw 128Kbit/s queue 8Kbytes<br>
  ipfw pipe 2 config bw 512Kbit/s queue 24Kbytes

ipfw queue 1 config pipe 1 weight 5 queue 4Kbytes mask src-ip 0xffffffff<br>
  ipfw queue 2 config pipe 2 weight 5 queue 8Kbytes mask dst-ip 0xffffffff<br>

  ipfw queue 3 config pipe 1 weight 3 queue 8Kbytes mask src-ip 0xffffffff<br>
  ipfw queue 4 config pipe 2 weight 3 queue 12Kbytes mask dst-ip 0xfffffff<br>
  ipfw queue 5 config pipe 1 weight 1 queue 4Kbytes mask src-ip 0xffffffff<br>
  ipfw queue 6 config pipe 2 weight 1 queue 8Kbytes mask dst-ip 0xffffffff

ipfw add 100 queue 1 udp from 192.168.0.0/24 to any out xmit xl0<br>
  ipfw add 150 skipto 1500 udp from 192.168.0.0/24 to any out xmit xl0<br>

  ipfw add 200 queue 2 udp from any to 192.168.0.0/24 in recv xl0<br>
  ipfw add 250 skipto 1500 udp from any to 192.168.0.0/24 in recv xl0<br>
  ipfw add 300 queue 3 tcp from 192.168.0.0/24 to any 80,25,110 out xmit xl0<br>
  ipfw add 350 skipto 1500 tcp from 192.168.0.0/24 to any 80,25,110 out xmit xl0<br>
  ipfw add 400 queue 4 tcp from any 80,25,110 to 192.168.0.0/24 in recv xl0<br>
  ipfw add 450 skipto 1500 tcp from any 80,25,110 to 192.168.0.0/24 in recv xl0<br>

  ipfw add 500 queue 5 ip from 192.168.0.0/24 to any out xmit xl0<br>
  ipfw add 600 queue 6 ip from any to 192.168.0.0/24 in recv xl0

ipfw add 1500 check-state<br>
  ipfw add 1600 allow ip from any to any via xl1<br>
  ipfw add 1700 reset tcp from any to 192.168.0.0/24,81.21.31.41 113 in recv xl0<br>
  ipfw add 1800 deny tcp from any to any 137,138,139,445<br>

  ipfw add 1900 deny udp from any to any 137,138,139<br>
  ipfw add 2000 allow tcp from any to 192.168.0.0/24,81.21.31.41 via xl0 established<br>
  ipfw add 2100 allow udp from any to 192.168.0.0/24,81.21.31.41 via xl0 established<br>
  ipfw add 2200 allow tcp from 192.168.0.0/24 to any via xl0 setup<br>
  ipfw add 2300 allow udp from 192.168.0.0/24 to any via xl0 setup<br>
  ipfw add 2400 allow icmp from any to any<br>

  ipfw add 2500 allow tcp from any to 81.21.31.41 22 via xl0 setup<br>
  ipfw add 65000 deny ip from any to any

Powyższa konfiguracja pozwala kolejkować ruch a jednocześnie chroni sieć.

Na zakończenie przedstawiam przykładowy skrypt dla bramy sieci LAN pozwalający
kolejkować ruch dynamicznie według usług. Nie opisuje go, gdyż po przeczytaniu
powyższego teksu powinieneś(aś) już sam(a) dojść do tego, za co odpowiadają
odpowiednie wpisy. Skrypt jest przewidziany dla łącza ADSL 128/512Kbit/s. W
razie niejasności co do składni polecam manual systemowy do ipfw ("man
ipfw").

#!/bin/sh<br>
  fwcmd=&quot;/sbin/ipfw&quot;

# Firewall quiet or normal?<br>
  ############################<br>
  if [ -n &quot;${1}&quot; ]; then<br>
  firewall_type=&quot;${1}&quot;<br>
  fi

case ${firewall_quiet} in<br>
  [Yy][Ee][Ss])<br>
  fwcmd=&quot;/sbin/ipfw -q&quot;<br>
  ;;<br>
  *)<br>
  fwcmd=&quot;/sbin/ipfw&quot;<br>

  ;;<br>
  esac

# Flush rules<br>
  ##############<br>
  ${fwcmd} -f flush<br>
  ${fwcmd} -f pipe flush<br>

  ${fwcmd} -f queue flush<br>
  ${fwcmd} zero

myip=&quot;81.21.31.41&quot;<br>
  siec=&quot;192.168.2.0/24&quot;<br>
  serwery_dns=&quot;81.21.31.42,81.21.31.132&quot;

iif=&quot;rl1&quot;<br>
  oif=&quot;rl0&quot;

poczta=&quot;25,110,119&quot;<br>
  dns=&quot;53&quot;<br>
  www=&quot;80,443,8080&quot;<br>

  ftp=&quot;20,21&quot;<br>
  ssh=&quot;22&quot;

### Flow<br>
  # - Wychodzace pakiety ACK maja najwyzszy priorytet<br>
  # - Zapytania i odpowiedzi DNS oraz ICMP maja wyzszy priorytet<br>
  # - SSH i poczta maja wysoki priorytet<br>

  # - ruch UDP (np. radio internetowe) ma normany priorytet<br>
  # - WWW i FTP maja normalny priorytet<br>
  # - Pozostale sesje TCP (np. GG) maja niski priorytet<br>
  ###

# interfejs &quot;loopback&quot;<br>
  ${fwcmd} add 100 set 1 allow ip from any to any via lo0<br>

  ${fwcmd} add 110 set 1 deny ip from any to 127.0.0.0/8<br>
  ${fwcmd} add 120 set 1 deny ip from 127.0.0.0/8 to any

# Rurka ruchu wychodzacego - 128Kbit/s<br>
  ${fwcmd} pipe 1 config bw 128Kbit/s queue 8Kbytes

# Rurka ruchu wchodzacego - 512Kbit/s<br>
  ${fwcmd} pipe 2 config bw 512Kbit/s queue 24Kbytes

<br>

  # Ruch wychodzacy<br>
  ##############<br>
  # --- ACK ---<br>
  ${fwcmd} queue 1 config weight 7 pipe 1 queue 3 buckets 64 mask src-ip 0x000000ff<br>
  # --- DNS, ICMP ---<br>
  ${fwcmd} queue 2 config weight 5 pipe 1 queue 3 buckets 64 mask src-ip 0x000000ff<br>

  # --- SSH, Poczta ---<br>
  ${fwcmd} queue 3 config weight 4 pipe 1 queue 4 buckets 64 mask src-ip 0x000000ff<br>
  # --- WWW, FTP ---<br>
  ${fwcmd} queue 4 config weight 3 pipe 1 queue 4 buckets 64 mask src-ip 0x000000ff<br>
  # --- Inne UDP ---<br>
  ${fwcmd} queue 5 config weight 1 pipe 1 queue 4 buckets 64 mask src-ip 0x000000ff<br>

  # --- Inne TCP ---<br>
  ${fwcmd} queue 6 config weight 1 pipe 1 queue 4 buckets 64 mask src-ip 0x000000ff<br>
  # --- Router lokalnie ---<br>
  ${fwcmd} queue 10 config weight 7 pipe 1 queue 4Kbytes

<br>
  # Ruch wchodzacy<br>
  ##############<br>

  # --- ACK ---<br>
  ${fwcmd} queue 11 config weight 7 pipe 2 queue 3 buckets 64 mask dst-ip 0x000000ff<br>
  # --- DNS, ICMP ---<br>
  ${fwcmd} queue 12 config weight 5 pipe 2 queue 3 buckets 64 mask dst-ip 0x000000ff<br>
  # --- SSH, Poczta ---<br>
  ${fwcmd} queue 13 config weight 4 pipe 2 queue 4 buckets 64 mask dst-ip 0x000000ff<br>

  # --- WWW, FTP ---<br>
  ${fwcmd} queue 14 config weight 3 pipe 2 queue 8 buckets 64 mask dst-ip 0x000000ff<br>
  # --- Inne UDP --<br>
  ${fwcmd} queue 15 config weight 1 pipe 2 queue 6 buckets 64 mask dst-ip 0x000000ff<br>
  # --- Inne TCP ---<br>
  ${fwcmd} queue 16 config weight 1 pipe 2 queue 6 buckets 64 mask dst-ip 0x000000ff<br>

  # --- Router lokalnie ---<br>
  ${fwcmd} queue 20 config weight 7 pipe 2 queue 20Kbytes

# Kolejki wychodzace<br>
  ###############<br>
  # --- Ruch wychodzacy z routera ---<br>
  ${fwcmd} add 500 queue 2 icmp from ${myip} to any out xmit ${oif}<br>

  ${fwcmd} add 600 skipto 10000 icmp from ${myip} to any out xmit ${oif}<br>
  ${fwcmd} add 700 queue 2 udp from ${myip} to any out xmit ${oif}<br>
  ${fwcmd} add 800 skipto 10000 udp from ${myip} to any out xmit ${oif}<br>
  ${fwcmd} add 900 queue 10 tcp from ${myip} to any out xmit ${oif}<br>
  ${fwcmd} add 1000 skipto 10000 tcp from ${myip} to any not ${ftp},${www} out
  xmit ${oif}<br>
  # --- DNS ---<br>

  ${fwcmd} add 1010 queue 12 udp from ${siec} to not ${siec} ${dns} out xmit ${oif}<br>
  ${fwcmd} add 1020 skipto 10000 udp from ${siec} to not ${siec} ${dns} out xmit
  ${oif}<br>
  ${fwcmd} add 1030 queue 12 tcp from ${siec} to not ${siec} ${dns} out xmit ${oif}<br>
  ${fwcmd} add 1040 skipto 10000 tcp from ${siec} to not ${siec} ${dns} out xmit
  ${oif}<br>
  # --- ICMP ---<br>
  ${fwcmd} add 1050 queue 12 icmp from ${siec} to not ${siec} out xmit ${oif}<br>

  ${fwcmd} add 1060 skipto 10000 icmp from ${siec} to not ${siec} out xmit ${oif}<br>
  # --- ACK ---<br>
  ${fwcmd} add 1070 queue 11 tcp from ${siec} to not ${siec} out xmit ${oif} iplen
  1-80 tcpflags ack<br>
  ${fwcmd} add 1080 skipto 10000 tcp from ${siec} to not ${siec} out xmit ${oif}
  iplen 1-80 tcpflags ack<br>
  # --- SSH, Poczta ---<br>
  ${fwcmd} add 1090 queue 13 tcp from ${siec} to not ${siec} ${ssh},${poczta} out
  xmit ${oif}<br>

  ${fwcmd} add 1100 skipto 10000 tcp from ${siec} to not ${siec} ${ssh},${poczta}
  out xmit ${oif}<br>
  # --- WWW, FTP ---<br>
  ${fwcmd} add 1110 queue 14 tcp from ${siec},${myip} to not ${siec} ${www} out
  xmit ${oif}<br>
  ${fwcmd} add 1120 skipto 10000 tcp from ${siec},${myip} to not ${siec} ${www}
  out xmit ${oif}<br>
  ${fwcmd} add 1130 queue 14 tcp from ${siec},${myip} to not ${siec} ${ftp} out
  xmit ${oif}<br>
  ${fwcmd} add 1140 skipto 10000 tcp from ${siec},${myip} to not ${siec} ${ftp}
  out xmit ${oif}<br>

  # --- Reszta UDP ---<br>
  ${fwcmd} add 1150 queue 15 udp from ${siec} to not ${siec} not ${dns} out xmit
  ${oif}<br>
  ${fwcmd} add 1160 skipto 10000 tcp from ${siec} to not ${siec} not ${dns} out
  xmit ${oif}<br>
  # --- Reszta TCP ---<br>
  ${fwcmd} add 1170 queue 15 tcp from ${siec} to not ${siec} not ${dns},${ssh},${poczta},${ftp},${www}
  out xmit ${oif}<br>
  ${fwcmd} add 1180 skipto 10000 tcp from ${siec} to not ${siec} not ${dns},${ssh},${poczta},${ftp},${www}
  out xmit ${oif}

# Kolejki przychodzace<br>
  #################<br>
  # --- Ruch przychodzacy do routera ---<br>
  ${fwcmd} add 1500 queue 32 icmp from any to ${myip} in recv ${oif}<br>
  ${fwcmd} add 1600 skipto 10000 icmp from any to ${myip} in recv ${oif}<br>
  ${fwcmd} add 1700 queue 32 udp from any to ${myip} in recv ${oif}<br>

  ${fwcmd} add 1800 skipto 10000 udp from any to ${myip} in recv ${oif}<br>
  ${fwcmd} add 1900 queue 20 tcp from any not ${ftp},${www} to ${myip} in recv
  ${oif}<br>
  ${fwcmd} add 2000 skipto 10000 tcp from any not ${ftp},${www} to ${myip} in
  recv ${oif}<br>
  # --- DNS ---<br>
  ${fwcmd} add 2010 queue 2 udp from not ${siec} ${dns} to ${siec} in recv ${oif}<br>
  ${fwcmd} add 2020 skipto 10000 udp from not ${siec} ${dns} to ${siec} in recv
  ${oif}<br>

  ${fwcmd} add 2030 queue 2 tcp from not ${siec} ${dns} to ${siec} in recv ${oif}<br>
  ${fwcmd} add 2040 skipto 10000 tcp from not ${siec} ${dns} to ${siec} in recv
  ${oif}<br>
  # --- ICMP ---<br>
  ${fwcmd} add 2050 queue 2 icmp from not ${siec} to ${siec} in recv ${oif}<br>
  ${fwcmd} add 2060 skipto 10000 icmp from not ${siec} to ${siec} in recv ${oif}<br>
  # --- ACK ---<br>

  ${fwcmd} add 2070 queue 1 tcp from not ${siec} to ${siec} in recv ${oif} iplen
  1-80 tcpflags ack<br>
  ${fwcmd} add 2080 skipto 10000 tcp from not ${siec} to ${siec} in recv ${oif}
  iplen 1-80 tcpflags ack<br>
  # --- SSH, Poczta ---<br>
  ${fwcmd} add 2090 queue 3 tcp from not ${siec} ${ssh},${poczta} to ${siec} in
  recv ${oif}<br>
  ${fwcmd} add 2100 skipto 10000 tcp from not ${siec} ${ssh},${poczta} to ${siec}
  in recv ${oif}<br>
  # --- WWW, FTP ---<br>

  ${fwcmd} add 2110 queue 4 tcp from not ${siec} ${www} to ${siec},${myip} in
  recv ${oif}<br>
  ${fwcmd} add 2120 skipto 10000 tcp from not ${siec} ${www} to ${siec},${myip}
  in recv ${oif}<br>
  ${fwcmd} add 2130 queue 4 tcp from not ${siec} ${ftp} to ${siec},${myip} in
  recv ${oif}<br>
  ${fwcmd} add 2140 skipto 10000 tcp from not ${siec} ${ftp} to ${siec},${myip}
  in recv ${oif}<br>
  # --- Reszta UDP ---<br>
  ${fwcmd} add 2150 queue 5 udp from not ${siec} not ${dns} to ${siec} in recv
  ${oif}<br>

  ${fwcmd} add 2160 skipto 10000 tcp from not ${siec} not ${dns} to ${siec} in
  recv ${oif}<br>
  # --- Reszta TCP ---<br>
  ${fwcmd} add 2170 queue 5 tcp from not ${siec} not ${dns},${ssh},${poczta},${ftp},${www}
  to ${siec} in recv ${oif}<br>
  ${fwcmd} add 2180 skipto 10000 tcp from not ${siec} not ${dns},${ssh},${poczta},${ftp},${www}
  to ${siec} in recv ${oif}

${fwcmd} add 10000 set 3 check-state

# SQUID<br>

  #==============<br>
  ${fwcmd} add 10100 fwd 192.168.2.2,8080 tcp from ${siec} to not ${siec} dst-port
  80,8080

# SSH i FTP<br>
  #==============<br>
  ${fwcmd} add 11100 set 2 allow log tcp from any to ${myip} 22 via ${oif} setup
  keep-state<br>
  ${fwcmd} add 11200 set 2 allow tcp from any to ${myip} 80 via ${oif} setup keep-state<br>

  ${fwcmd} add 11300 set 2 allow tcp from any ${ftp} to ${myip},${siec} in recv
  ${oif}<br>
  ${fwcmd} add 11400 set 2 allow udp from ${serwery_dns} ${dns} to ${myip},${siec}
  via ${oif}

# Statefull Firewall<br>
  #==============<br>
  ${fwcmd} add 12100 set 3 allow ip from any to any via ${iif}<br>
  ${fwcmd} add 12200 set 4 allow tcp from any to ${siec},${myip} via ${oif} established<br>

  ${fwcmd} add 12300 set 4 allow tcp from ${siec},${myip} to any out xmit ${oif}<br>
  ${fwcmd} add 12400 set 4 allow udp from ${siec},${myip} to any out xmit ${oif}

# Reset IDENTa<br>
  #==============<br>
  ${fwcmd} add 12500 set 5 reset log tcp from any to ${myip},${siec} 113 in recv
  ${oif}

# Wyblokuj wychodzacy NetBIOS<br>
  #==============<br>

  ${fwcmd} add 13100 set 5 deny tcp from any to any 137,138,139,445<br>
  ${fwcmd} add 13200 set 5 deny udp from any to any 137,138,139

# ICMP<br>
  #==============<br>
  ${fwcmd} add 13300 set 2 allow icmp from any to any

# Loguj co wyblokowane<br>
  #==============<br>

  ${fwcmd} add 65000 set 6 deny log ip from any to any

5. Dodatek

5.1. NATowe niuanse

Jeśli używamy do NAT-owania programu ipnat to adres źródłowy pakietu wychodzącego z sieci LAN będzie zamieniony zanim trafi do dummynetu, co czasami może uniemożliwić poprawne działanie naszych regułek sterujących ruchem. Można to zachowanie zmienić modyfikując jądro systemu (plik "netinet/ip_output.c") łatką Pawła Małachowskiego dostępną pod adresem "http://unia.3lo.lublin.pl/~pawmal/freebsd/" (UWAGA! Łatka może być nieaktualna, więc stosowanie jej w niewłaściwy sposób może doprowadzić do nieprawidłowego funkcjonowania jądra. Najlepiej zapoznać się z wprowadzanymi przez nią zmianami i dokonać ich samemu na odpowiednim pliku) tak by dla ruchu wychodzącego pakiety były kierowane najpierw przez dummynet, a dopiero później nat-owane.

5.2. Obliczanie rozmiaru bufora (queue) dla rurki lub kolejki

W powyższym tekście pojawia się pojęcie bufora (queue) dla rurki bądź kolejki. Bufor domyślnie (czyli jeśli my go w regułce nie zdefiniujemy) ma wielkość 50 "slotów". Slot to po prostu miejsce na jeden pakiet, czyli domyślnie mamy bufor o wielkości 50 pakietów. Przeważnie taka wielkość (50) jest za duża (50 pakietów po 1500 bajtów to 75-cio kilobajtowy bufor) dla łącza DSL (a takie tu rozważamy). Wielkość bufora można podawać zarówno w slotach (np. queue 10 oznacza bufor na 10 pakietów) albo w kilobajtach tak, jak w przykładach, gdzie jest on odgórnie przeze mnie zdefiniowany. Wielkość bufora można wyliczyać samemu w zależności od tego, jakim łączem dysponujemy i jakie warunki w sieci chcemy uzyskać. Parametr ten można obliczyć dzieląc przepustowość łącza w kilobajtach w danym kierunku ("in" lub "out") na 3 (lub na 4 jeśli chcemy uzyskać większą interaktywność kosztem przepustowości). Dlaczego? Spróbujmy obliczyć wielkość bufora dla pakietów przychodzących ("in") na łączu 512Kbit/s. 512Kbitów/s to 64KBajty/s. Łącze takie może przyjąć 16KB danych w ciągu 250ms, czyli ustawienie bufora na 16KB pozwoli na buforowanie transmisji przez 250ms. Oznacza to, że nasz dummynet będzie generował maksymalne opóźnienie 250ms w jedną stronę, co dla ruchu WWW jest akceptowalne. Jeśli chcemy uzyskać lepsze warunki dla np. gier sieciowych należy kolejce lub rurce przypisać odpowiedni bufor (np. do buforowania transmisji przez maksymalnie 50ms). W związku z tym, że łącze ADSL 128/512Kbit/s jest łączem asynchronicznym opóźnienie transmisji będzie sumą opóźnienia przy ruchu wychodzącym i wchodzącym. Dlatego ustawienie zbyt małej wartości buforów może doprowadzić do znacznego spadku transmisji a ustawienie go na zbyt dużą wartość do zbyt dużych opóźnień (nie akceptowalnych np. przez graczy sieciowych).

6. Na zakończenie

Wiem, że powyższy tekst nie przedstawia wszystkich aspektów omawianego zagadnienia lecz tylko te najważniejsze z punktu widzenia początkującego Administratora lub Użytkownika FreeBSD. Jeśli nie znalazłeś(aś) w tym tekście czegoś lub czujesz niedosyt wiedzy polecam zaznajomić się z dokumentacją systemową (manual) dotyczącą ipfw oraz opracowaniami związanymi z tym zagadnieniem zamieszczonymi w Internecie.

Tekst ten pisany był "z głowy" w oparciu o własne doświadczenie oraz własne skrypty, więc może zawierać błędy i/lub niedociągnięcia. Jeśli takowe znajdziesz bardzo proszę o kontakt - postaram się nanieść poprawki.

Autor: 
Tomasz Król [yautja(at)interia.pl]
Porozmawiaj o tym artykule na forum: 

tytus, nie., 20/04/2008 - 15:55